/***************************************************************************
 *   Copyright (C) 2004 - 2005 by Raphael Langerhorst                      *
 *   raphael-langerhorst@gmx.at                                            *
 *                                                                         *
 *   Permission is hereby granted, free of charge, to any person obtaining *
 *   a copy of this software and associated documentation files (the       *
 *   "Software"), to deal in the Software without restriction, including   *
 *   without limitation the rights to use, copy, modify, merge, publish,   *
 *   distribute, sublicense, and/or sell copies of the Software, and to    *
 *   permit persons to whom the Software is furnished to do so, subject to *
 *   the following conditions:                                             *
 *                                                                         *
 *   The above copyright notice and this permission notice shall be        *
 *   included in all copies or substantial portions of the Software.       *
 *                                                                         *
 *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       *
 *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    *
 *   MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*
 *   IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR     *
 *   OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, *
 *   ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR *
 *   OTHER DEALINGS IN THE SOFTWARE.                                       *
 ***************************************************************************/
 
#include "GCoreXmlSerializer.h"

#include <GweController.h>
#include <GweSimpleController.h>
#include <GDataController.h>
#include <GWorldData.h>

#include <GAttractAgent.h>
#include <GDynamicGeneratorAgent.h>
#include <GEnergyFormAgent.h>
#include <GMoveAgent.h>
#include <GRadiatingAgent.h>

#include <qstring.h>
#include <qdom.h>
#include <iostream>

using namespace std;
using namespace GCS;
using namespace GBE;

namespace GWE
{

GCoreXmlSerializer::GCoreXmlSerializer(const GCS::GWorldData* world_data, QObject *parent, const char *name)
: QObject(parent,name),
  WorldData(world_data)
{
}


/*!
    \fn GWE::GCoreXmlSerializer::~GCoreXmlSerializer()
 */
GCoreXmlSerializer::~GCoreXmlSerializer()
{
    
}

/*!
    \fn GWE::GCoreXmlSerializer::createAgent(QDomElement data)
 */
GCS::GAgent* GCoreXmlSerializer::createAgent(QDomElement data)
{
  //@todo for 0.6 release: temporary function till we get an agent factory to create the appropriate subagent
  
  QString name = data.namedItem("name").toElement().text();
  
  if (name.isEmpty())
  {
    qWarning("Agent name is empty!");
  }

  /*  
  GAttractAgent
  GDynamicGeneratorAgent
  GMoveAgent
  GRadiatingAgent
  */
  
  if (name == "GBE::GAttractAgent")
  {
    return new GAttractAgent();
  }
  else if (name == "GBE::GDynamicGeneratorAgent")
  {
    return new GDynamicGeneratorAgent();
  }
  else if (name == "GBE::GEnergyFormAgent")
  {
    return new GEnergyFormAgent();
  }
  else if (name == "GBE::GMoveAgent")
  {
    return new GMoveAgent();
  }
  else if (name == "GBE::GRadiatingAgent")
  {
    return new GRadiatingAgent();
  }
  else
  {
    qWarning(QString("Couldn't determine appropriate agent for given agent name: %1").arg(name));
    return NULL;
  }
}

/*!
    \fn GWE::GCoreXmlSerializer::createAgent(QDomElement data)
 */
QPtrList<GCS::GAgent>* GCoreXmlSerializer::createAgents(QDomElement data)
{
    if (data.isNull())
      return NULL;
    
    QPtrList<GCS::GAgent>* agents = new QPtrList<GCS::GAgent>;
    
    QDomNode agentNode = data.firstChild();
    
    while( !agentNode.isNull() ) 
    {
      if(agentNode.isElement())
      {
        agents->append(createAgent(agentNode.toElement()));
      }
      agentNode = agentNode.nextSibling();
    }
    return agents;

}

/*!
    \fn GWE::GCoreXmlSerializer::createElementData(QDomElement data)
 */
QDomDocument* GCoreXmlSerializer::createElementData(QDomElement data)
{
    if (data.isNull())
    {
      qWarning("Element Data to deserialize is NULL! Returning NULL.");
      return NULL;
    }
//     else
//     {
//       qDebug("deserializing element data:");
//       qDebug(data.ownerDocument().toString());
//     }
    QDomDocument* NewDocument = new QDomDocument();

    QDomNode child = data.firstChild();
    while (!child.isNull())
    {
      if (child.isElement())
        NewDocument->appendChild(NewDocument->importNode(child,true));
      child = child.nextSibling();
    }

//     if (!NewDocument->isNull())
//     {
//       qDebug("deserialized element data:");
//       qDebug(NewDocument->toString());
//     }
    
    return NewDocument;
    
// @todo remove, GElementData is now a QDomDocument
//     GCS::GElementData* elementData = new GCS::GElementData();
//     elementData->setDomDocument(*NewDocument);
//     return elementData;
}


/*!
    \fn GWE::GCoreXmlSerializer::createElement(QDomElement data)
 */
GCS::GElement* GCoreXmlSerializer::createElement(QDomElement data)
{
    if (data.isNull())
      return NULL;
    
    QPtrList<GCS::GAgent>* Agents = createAgents(data.namedItem("Agents").toElement());

    if (Agents==NULL)
    {
      return NULL;
    }
    
    GCS::GObject * object = createObject(data.namedItem("Object").toElement());

    if (object==NULL)
    {
      delete Agents;
      return NULL;
    }
    
    GCS::GElement * element = new GCS::GElement(object);

    if (element==NULL)
    {
      delete Agents;
      delete object;
      return NULL;
    }
    
    GCS::GAgent *Agent;
    for ( Agent = Agents->first(); Agent; Agent = Agents->next() )
    {
        element->addAgent(Agent);
    }
    
    return element;
}

GCS::GElementID GCoreXmlSerializer::createElementID(QDomElement data, bool* ok)
{
  if (ok!=NULL)
    *ok=true;
  if (data.isNull())
  {
    if (ok!=NULL)
      *ok=false;
    return GCS::GElementID(0);
  }
  else
    return GCS::GElementID(data.text().toULong());
}



/*!
    \fn GWE::GCoreXmlSerializer::createEnergy(QDomElement data)
 */
GCS::GEnergy* GCoreXmlSerializer::createEnergy(QDomElement data)
{
  if (data.isNull())
    return NULL;
  bool ok=false;
  double level = data.namedItem("Level").toElement().text().toDouble(&ok);
  if (!ok)
    return NULL;
  double amount = data.namedItem("Amount").toElement().text().toDouble(&ok);
  if (!ok)
    return NULL;
  double sigma = data.namedItem("Sigma").toElement().text().toDouble(&ok);
  if (!ok)
    return NULL;
  
  return new GCS::GEnergy(level,amount,sigma);
}


/*!
    \fn GWE::GCoreXmlSerializer::createForm(QDomElement data)
 */
GCS::GForm* GCoreXmlSerializer::createForm(QDomElement data)
{
    QDomElement DomPosition = data.namedItem("Position").toElement();
    QDomElement DomRotation = data.namedItem("Rotation").toElement();
    QDomElement DomEllipsoid = data.namedItem("Ellipsoid").toElement();

    if (DomPosition.isNull() || DomRotation.isNull() || DomEllipsoid.isNull())
      return NULL;

    bool ok;
    

//More efficient than making an extra function call to createVector3???
    GCS::GVector3 position(this->createVector3(DomPosition,&ok));
    if (!ok)
      return NULL;
    GCS::GVector3 rotation(this->createVector3(DomRotation,&ok));
    if (!ok)
      return NULL;
    GCS::GVector3 ellipsoid(this->createVector3(DomEllipsoid,&ok));
    if (!ok)
      return NULL;
    
    return new GCS::GForm(
            position,
            rotation,
            ellipsoid);

}
/*!
    \fn GWE::GCoreXmlSerializer::createVector3(QDomElement data)
 */
GCS::GVector3 GCoreXmlSerializer::createVector3(QDomElement data, bool* ok)
{
  if (ok!=NULL)
    *ok=true;
  bool ok_private=false;
  double x = data.namedItem("x").toElement().text().toDouble(&ok_private);
  if (!ok_private)
  {
    if (ok!=NULL)
      *ok=false;
  }
  double y = data.namedItem("y").toElement().text().toDouble(&ok_private);
  if (!ok_private)
  {
    if (ok!=NULL)
      *ok=false;
  }
  double z = data.namedItem("z").toElement().text().toDouble(&ok_private);
  if (!ok_private)
  {
    if (ok!=NULL)
      *ok=false;
  }
  return GCS::GVector3(x,y,z);
}

/*!
    \fn GWE::GCoreXmlSerializer::createObject(QDomElement data)
 */
GCS::GObject* GCoreXmlSerializer::createObject(QDomElement data)
{
    GCS::GEnergy * energy = createEnergy(data.namedItem("Energy").toElement());

    if (energy==NULL)
      return NULL;

    GCS::GForm * form = createForm(data.namedItem("Form").toElement());

    if (form==NULL)
    {
      delete energy;
      return NULL;
    }

    bool ok=false;
    GCS::GElementID  parent = createElementID(data.namedItem("Parent").toElement(),&ok);
    if (!ok)
    {
      delete energy;
      delete form;
      return NULL;
    }
    GCS::GElementID  ID = createElementID(data.namedItem("ID").toElement(),&ok);
    if (!ok)
    {
      delete energy;
      delete form;
      return NULL;
    }
    GCS::GElementID  connection = createElementID(data.namedItem("Connection").toElement(),&ok);
    if (!ok)
    {
      delete energy;
      delete form;
      return NULL;
    }
    
    QDomDocument * mydata = createElementData(data.namedItem("Data").toElement());
    if (mydata == NULL)
    {
      delete energy;
      delete form;
      return NULL;
    }
    GCS::GObject* object = new GCS::GObject(energy,form,parent,ID,connection,mydata,WorldData);

    if (object == NULL)
    {
      delete energy;
      delete form;
      delete mydata;
      return NULL;
    }
    
    return object;
}

GCS::GMatrix44 GCoreXmlSerializer::createMatrix44(QDomElement data, bool* ok)
{
  if (ok!=NULL)
    *ok=true;
  bool ok_private=false;
  double m[4][4];
  for (int i = 0; i<4; i++)
  {
    for (int j = 0; j<4; j++)
    {
      m[i][j] = data.namedItem(QString("m%1%2").arg(QString::number(i+1)).arg(QString::number(j+1))).toElement().text().toDouble(&ok_private);
      if (!ok_private)
      {
        if (ok!=NULL)
          *ok=false;
      }
    }
  }
  return GCS::GMatrix44(m[0][0],m[0][1],m[0][2],m[0][3],
                        m[1][0],m[1][1],m[1][2],m[1][3],
                        m[2][0],m[2][1],m[2][2],m[2][3],
                        m[3][0],m[3][1],m[3][2],m[3][3]);
}

GCS::GElementInfluence GCoreXmlSerializer::createElementInfluence(QDomElement data, bool* ok)
{
  if (ok!=NULL)
    *ok=true;
  GCS::GEnergy* e = this->createEnergy(data.namedItem("Energy").toElement());
  GCS::GEnergy energy;
  if (e==NULL)
  {
    if (ok!=NULL)
      *ok=false;
  }
  else
  {
    energy.set(*e);
  }
  delete e;
  e=NULL;
  GCS::GElementID id = this->createElementID(data.namedItem("Source").toElement(),ok);
  return GCS::GElementInfluence(id,energy);
}

GCS::GElementID GCoreXmlSerializer::getInfluenceTarget(QDomElement data, bool* ok)
{
  return GCS::GElementID(data.attribute("target","0").toULong(ok));
}

/*!
    \fn GWE::GCoreXmlSerializer::serializeAgent(GCS::GAgent* agent, QString TagName, QDomDocument* document)
 */
QDomElement GCoreXmlSerializer::serializeAgent(const GCS::GAgent* agent, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );

    document->appendChild( root );

    QDomElement Name = document->createElement( "name" );
    root.appendChild( Name );
    Name.appendChild( document->createTextNode( agent->className() ) );

    return root;
}

/*!
    \fn GWE::GCoreXmlSerializer::serializeElementData(GCS::GElementData* data)
 */
QDomElement GCoreXmlSerializer::serializeElementData(const QDomDocument* data, QString TagName, QDomDocument* document)
{
  if (data->isNull())
  {
    qWarning("Element Data to serialize is NULL!");
  }
  Q_CHECK_PTR(data);
  QDomElement e = document->createElement(TagName);
  document->appendChild(e);
  QDomNode child = data->firstChild();
  while (!child.isNull())
  {
    if (child.isElement())
      e.appendChild(document->importNode(child,true));
    child = child.nextSibling();
  }
//   if (!data->isNull())
//   {
//     qDebug("original element data:");
//     qDebug(data->toString());
//     qDebug("serialized element data:");
//     qDebug(document->toString());
//   }
  return e;
}


/*!
    \fn GWE::GCoreXmlSerializer::serializeElement(GCS::GElement* element)
 */
QDomElement GCoreXmlSerializer::serializeElement(const GCS::GElement* element, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );
    document->appendChild( root );
    
    root.appendChild(serializeObject(element->getObject(),QString("Object"),document));
    root.appendChild(serializeAgents(element->getAgents(),QString("Agents"),document));
    
    root.setAttribute(QString("ParkTime"),element->getParkTime().toString());    
    return root;
}


QDomElement GCoreXmlSerializer::serializeElementID(const GCS::GElementID& element_id, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );
    document->appendChild( root );

    root.appendChild(document->createTextNode(QString::number(element_id.getID())));

    return root;
}


/*!
    \fn GWE::GCoreXmlSerializer::serializeEnergy(GCS::GEnergy* energy)
 */
QDomElement GCoreXmlSerializer::serializeEnergy(const GCS::GEnergy* energy, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );

    document->appendChild( root );

    QString LevelString; 
    QString AmountString; 
    QString SigmaString; 
    
    LevelString.setNum(energy->level()); 
    AmountString.setNum(energy->amount()); 
    SigmaString.setNum(energy->sigma());     

    QDomElement Level = document->createElement( "Level" );
    root.appendChild( Level );
    Level.appendChild( document->createTextNode( LevelString ) );
    
    QDomElement Amount = document->createElement( "Amount" );
    root.appendChild( Amount );
    Amount.appendChild( document->createTextNode( AmountString ) );
    
    QDomElement Sigma = document->createElement( "Sigma" );
    root.appendChild( Sigma );
    Sigma.appendChild( document->createTextNode( SigmaString ) );

    return root;
}


/*!
    \fn GWE::GCoreXmlSerializer::serializeForm(GCS::GForm* form)
 */
QDomElement GCoreXmlSerializer::serializeForm(const GCS::GForm* form, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );
    document->appendChild( root );   
    
    root.appendChild( serializeVector3(&(form->Position),QString("Position"),document));
    root.appendChild( serializeVector3(&(form->Rotation),QString("Rotation"),document));
    root.appendChild( serializeVector3(&(form->Ellipsoid),QString("Ellipsoid"),document));

    return root;
}
/*!
    \fn GWE::GCoreXmlSerializer::serializeVector3(GCS::GVector3* vector3)
 */
QDomElement GCoreXmlSerializer::serializeVector3(const GCS::GVector3* vector3, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );
    document->appendChild( root );

    QString XString; 
    QString YString; 
    QString ZString; 
    
    XString.setNum(vector3->x); 
    YString.setNum(vector3->y); 
    ZString.setNum(vector3->z);     

    QDomElement X = document->createElement( "x" );
    root.appendChild( X );
    X.appendChild( document->createTextNode( XString ) );
    
    QDomElement Y = document->createElement( "y" );
    root.appendChild( Y );
    Y.appendChild( document->createTextNode( YString ) );
    
    QDomElement Z = document->createElement( "z" );
    root.appendChild( Z );
    Z.appendChild( document->createTextNode( ZString ) );

    return root;
}

QDomElement GCoreXmlSerializer::serializeObject(const GCS::GObject* object, QString TagName, QDomDocument* document)
{

    QDomElement root = document->createElement( TagName );
    document->appendChild( root );
    
    root.appendChild(serializeEnergy(object->getEnergy(),QString("Energy"),document));
    root.appendChild(serializeForm(object->getForm(),QString("Form"),document));
    root.appendChild(serializeElementID(object->getParent(),QString("Parent"),document));
    root.appendChild(serializeElementID(object->getID(),QString("ID"),document));
    root.appendChild(serializeElementID(object->getConnection(),QString("Connection"),document));
    root.appendChild(serializeElementData(object->getElementData(),QString("Data"),document));
    
    return root;
}

QDomElement GCoreXmlSerializer::serializeAgents(QValueList<const GCS::GAgent*> Agents, QString TagName, QDomDocument* document)
{
    QDomElement root = document->createElement( TagName );
    document->appendChild( root );
    
    QValueList<const GCS::GAgent*>::iterator it;
    for ( it = Agents.begin(); it != Agents.end(); ++it )
    {
        root.appendChild(serializeAgent(*it,QString("Agent"),document));
    }
    
    return root;
}

QDomElement GCoreXmlSerializer::serializeMatrix44(const GCS::GMatrix44& matrix, QString TagName, QDomDocument* document)
{
  //@todo implement
  QDomElement root = document->createElement(TagName);
  document->appendChild(root);
  for (int i=0; i<4; i++)
  {
    for (int j=0; j<4; j++)
    {
      QDomElement e = document->createElement(QString("m")+QString::number(i+1)+QString::number(j+1));
      root.appendChild(e);
      e.appendChild(document->createTextNode(QString::number(matrix.m[i][j])));
    }
  }
  return root;
}
    
QDomElement GCoreXmlSerializer::serializeElementInfluence(const GCS::GElementInfluence& influence, const GCS::GElementID& target_element, QString TagName, QDomDocument* document)
{
  //@todo implement
  // IMPORTANT: the target_element should be modelled as an "attribute" of the root DOM element;
  //            the GElementInfluence class does not hold any target IDs, thus we need to provide
  //            this additional info to be able to reconstruct the target at other servers (in createElementInfluence())
  QDomElement root = document->createElement(TagName);
  document->appendChild(root);
  root.setAttribute("target",QString::number(target_element.getID()));
  root.appendChild(this->serializeElementID(influence.source(),"Source",document));
  root.appendChild(this->serializeEnergy(&influence.Energy,"Energy",document));
  return root;
}

}


syntax highlighted by Code2HTML, v. 0.9.1