/***************************************************************************
 *   Copyright (C) 2004 by Hans Oischinger                                 *
 *   hans.oischinger@kdemail.net                                           *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA.             *
 ***************************************************************************/
#include "esetgenericattrcmd.h"

// Core
#include "core/direktorinstance.h"
#include "core/elementdatabase.h"
#include "core/timeserver.h"

// Elements
#include "elements/elementnode.h"
#include "elements/attribute.h"
#include "elements/timeattribute.h"
#include "elements/lengthattribute.h"
#include "elements/angle.h"
#include "elements/animatedcolorattr.h"
#include "elements/animatedenum.h"
#include "elements/animatedlengthattr.h"
#include "elements/animatednumber.h"
#include "elements/animatedtransformlist.h"
#include "elements/animateelement.h"
#include "elements/colorattribute.h"
#include "elements/enumeration.h"
#include "elements/fillattribute.h"
#include "elements/strokeattribute.h"
#include "elements/transformlist.h"
#include "elements/urireference.h"
#include "elements/number.h"

// KDE
#include <kdebug.h>
#include <klocale.h>

ESetGenericAttrCMD::ESetGenericAttrCMD( ElementNode* element, Attribute &targetattr, Attribute &newattr ) :
    ECommand( i18n( "Set Generic Attribute" ) ),
    AVisitor(),
    m_element(element),
    m_newAttr(0),
    m_oldAttr(0),
    m_animElement(0),
    m_removed(0)
{
  m_targetAttr = &targetattr;
  m_oldAttr = targetattr.clone();
  m_newAttr = newattr.clone();
}

ESetGenericAttrCMD::~ESetGenericAttrCMD()
{
  if (m_removed)
    Kast::self()->elementDB().deleteEntry( m_animElement );
  delete m_newAttr;
  delete m_oldAttr;
}

void ESetGenericAttrCMD::execute( )
{
  ECommand::execute();
  m_targetAttr->accept(*this);
  if ( m_element )
  {
    m_element->setDirty();
    Kast::self()->elementDB().entryModified(m_element);
  }
}

void ESetGenericAttrCMD::unexecute( )
{
  ECommand::unexecute();
  m_targetAttr->accept(*this);
  if ( m_element )
  {
    m_element->setDirty();
    Kast::self()->elementDB().entryModified(m_element);
  }
}

bool ESetGenericAttrCMD::visit( TimeAttribute &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(TimeAttribute)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = * static_cast<TimeAttribute*>(m_newAttr);
  }
  else
  {
    a = *static_cast<TimeAttribute*>(m_oldAttr);
  }
  return true;
}

bool ESetGenericAttrCMD::visit( LengthAttribute &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(LengthAttribute)" << endl;

  if (isExecuting() || isRedoing())
  {
    a = *static_cast<LengthAttribute*>(m_newAttr);
  }
  else
  {
    a = *static_cast<LengthAttribute*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( Angle &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(Angle)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<Angle*>(m_newAttr);
  }
  else
  {
    a = *static_cast<Angle*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( AnimatedColor &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(AnimatedColor)" << endl;
  if (isExecuting() || isRedoing())
  {
    a.baseVal() = *static_cast<ColorAttribute*>(m_newAttr);
  }
  else
  {
    a.baseVal() = *static_cast<ColorAttribute*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( AnimatedEnum &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(AnimatedEnum)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<AnimatedEnum*>(m_newAttr);
  }
  else
  {
    a = *static_cast<AnimatedEnum*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( AnimatedLength &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(LengthAttribute)" << endl;

  if ( getRecordAnimation() )
  {
    if (isExecuting() || isRedoing())
    {
      // Redo
      if ( m_animElement )
      {
        Kast::self()->elementDB().reviveEntry( m_animElement );
        m_removed = false;
      }
      else
      {
        AnimatedLength* targetAttr = &a;
        LengthAttribute *oldAttr = &targetAttr->baseVal();
        LengthAttribute *newAttr = static_cast<LengthAttribute*>(m_newAttr);
        if ( *oldAttr != *newAttr )
        {
          URIReference *ref = new URIReference( m_element, true );
          AnimateLengthElement *animLengthElement = new AnimateLengthElement(*ref, targetAttr );
          m_animElement = animLengthElement;
          animLengthElement->setFill( AnimateElement::FILL_FREEZE );
          animLengthElement->toAttribute() = *newAttr;
          finalizeAnimateElement();
        }
      }
    }
    else
    {
      Kast::self()->elementDB().removeEntry( m_animElement );
      m_removed = true;
    }
  }
  else
  {
    if (isExecuting() || isRedoing())
    {
      a.baseVal() = *static_cast<LengthAttribute*>(m_newAttr);
    }
    else
    {
      a.baseVal() = *static_cast<LengthAttribute*>(m_oldAttr);
    }
  }
  return true;
}


bool ESetGenericAttrCMD::visit( AnimatedNumber &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(AnimatedNumber)" << endl;

  if ( getRecordAnimation() )
  {
    if (isExecuting() || isRedoing())
    {
      // Redo
      if ( m_animElement )
      {
        Kast::self()->elementDB().reviveEntry( m_animElement );
        m_removed = false;
      }
      else
      {
        AnimatedNumber* targetAttr = &a;
        Number *oldAttr = &targetAttr->baseVal();
        Number *newAttr = static_cast<Number*>(m_newAttr);
        if ( *oldAttr != *newAttr )
        {
          URIReference *ref = new URIReference( m_element, true );
          AnimateNumberElement *animNumElement = new AnimateNumberElement(*ref, targetAttr );
          m_animElement = animNumElement;
          animNumElement->setFill( AnimateElement::FILL_FREEZE );
          animNumElement->toAttribute() = *newAttr;
          finalizeAnimateElement();
        }
      }
    }
    else
    {
      Kast::self()->elementDB().removeEntry( m_animElement );
      m_removed = true;
    }
  }
  else
  {
    if (isExecuting() || isRedoing())
    {
      a.baseVal() = *static_cast<Number*>(m_newAttr);
    }
    else
    {
      a.baseVal() = *static_cast<Number*>(m_oldAttr);
    }
  }
  return true;
}


bool ESetGenericAttrCMD::visit( AnimatedTransformList &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(AnimatedTransformList)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<AnimatedTransformList*>(m_newAttr);
  }
  else
  {
    a = *static_cast<AnimatedTransformList*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( ColorAttribute &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(ColorAttribute)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<ColorAttribute*>(m_newAttr);
  }
  else
  {
    a = *static_cast<ColorAttribute*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( Enumeration &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(Enumeration)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<Enumeration*>(m_newAttr);
  }
  else
  {
    a = *static_cast<Enumeration*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( FillAttribute &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(FillAttribute)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<FillAttribute*>(m_newAttr);
  }
  else
  {
    a = *static_cast<FillAttribute*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( Number &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(Number)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<Number*>(m_newAttr);
  }
  else
  {
    a = *static_cast<Number*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( StrokeAttribute &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(StrokeAttribute)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<StrokeAttribute*>(m_newAttr);
  }
  else
  {
    a = *static_cast<StrokeAttribute*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( Transform &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(Transform)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<Transform*>(m_newAttr);
  }
  else
  {
    a = *static_cast<Transform*>(m_oldAttr);
  }
  return true;
}


bool ESetGenericAttrCMD::visit( TransformList &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(TransformList)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<TransformList*>(m_newAttr);
  }
  else
  {
    a = *static_cast<TransformList*>(m_oldAttr);
  }
  return true;
}



bool ESetGenericAttrCMD::visit( URIReference &a )
{
  kdDebug() << "ESetGenericAttrCMD::visit(URIReference)" << endl;
  if (isExecuting() || isRedoing())
  {
    a = *static_cast<URIReference*>(m_newAttr);
  }
  else
  {
    a = *static_cast<URIReference*>(m_oldAttr);
  }
  return true;
}

void ESetGenericAttrCMD::finalizeAnimateElement()
{
  if (!m_animElement)
  {
    kdWarning() << "ESetGenericAttrCMD::finalizeAnimateElement() - called on NULL m_animElement" << endl;
  }
  // set begin and duration
  double startTime = Kast::self()->timeServer().calcAnimStartTime( m_element ).value();
  double currTime = Kast::self()->timeServer().getTime();
  m_animElement->getStartTime().setValue( startTime );
  m_animElement->getSimpleDuration().setValue( currTime - startTime );

  m_element->appendChild(m_animElement);
  Kast::self()->elementDB().insertEntry(m_animElement);
}
