/***************************************************************************
 *   Copyright (C) 2003-2004 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 "GAgent.h"
#include "GForm.h"
#include "GObject.h"
#include "GEnergy.h"
#include "GElementID.h"

#include <qdom.h>

#include <exception>

using namespace GCS;

using namespace std;


namespace GCS
{

GAgent::GAgent(QObject* parent, const char* name)
: QObject(parent,name),
  Object(NULL),
  Agents(NULL),
  shutdown(FALSE)
{
}

GObject* GAgent::requestObject()
{
  Q_CHECK_PTR(Object);
  if (Object)
    return Object;
  else
    throw exception();
}

void GAgent::run()
{
}

GEnergy* GAgent::requestEnergy()
{
  Q_CHECK_PTR(Object);
  if (Object==NULL)
    throw exception();
  
  if (Object->hasEnergy() == FALSE)
    throw exception();
    
  return Object->getEnergy();
}

GForm* GAgent::requestForm()
{
  Q_CHECK_PTR(Object);
  if (Object==NULL)
    throw exception();
  if (Object->hasForm() == FALSE)
    throw exception();
  return Object->getForm();
}

QValueList<const GAgent*> GAgent::requestAgents() const
{
  Q_CHECK_PTR(Agents);
  if (Agents==NULL)
    throw exception();
  
  QValueList<const GAgent*> list;
  QValueList<GAgent*>::iterator it;
  for (it = this->Agents->begin(); it != this->Agents->end(); ++it)
  {
    list.append(*it);
  }
  return list;
}

const GElementID& GAgent::getElementID() const
{
  Q_CHECK_PTR(Object);
  if (Object==NULL)
    throw exception();
  return Object->getID();
}

const GElementID& GAgent::getConnectionID() const
{
  Q_CHECK_PTR(Object);
  if (Object==NULL)
    throw exception();
  return Object->getConnection();
}

//BEGIN Helper member functions for working with ElementData

QDomElement GAgent::xmlGetTopElement(QString tag_name, bool& ok)
{
  if (tag_name.isEmpty())
  {
    qWarning("Tag name is empty!");
    ok=false;
    return QDomElement();
  }
  QDomDocument* data = this->requestObject()->getElementData();
  if (data->isNull())
  {
    qWarning("Element data is a NULL document!");
    ok=false;
    return QDomElement();
  }
  else
  {
    QDomElement e = data->elementsByTagName(tag_name).item(0).toElement();
    if (e.isNull())
    {
//       qDebug(QString("Top tag %1 did not exist, created on the fly").arg(tag_name));
      e = data->createElement(tag_name);
      data->appendChild(e);
    }
    ok=true;
    return e;
  }
}

QDomElement GAgent::xmlGetElement(QDomElement parent, QString child_tag_name, bool& existed)
{
  QDomElement e = parent.elementsByTagName(child_tag_name).item(0).toElement();
  if (e.isNull())
  {
//     qDebug(QString("Child tag %1 did not exist, created on the fly").arg(child_tag_name));
    e = parent.ownerDocument().createElement(child_tag_name);
    parent.appendChild(e);
    existed=false;
  }
  else
    existed=true;
  return e;
}

QDomElement GAgent::xmlGetElement(QString xpath, bool& existed)
{
  if (xpath.isEmpty())
  {
    qWarning("xpath is empty!");
    existed=false;
    return QDomElement();
  }
  
//   qDebug(xpath);
  
  QChar s('/');
  
  QDomElement element = this->xmlGetTopElement(xpath.section(s,0,0,QString::SectionSkipEmpty),existed);
  xpath = xpath.section(s,1,0xffffffff,QString::SectionSkipEmpty);
  
  Q_ASSERT(!element.isNull());
  
  while (!xpath.isEmpty())
  {
//     qDebug(QString("remaining xpath: %1").arg(xpath));
    QString tag_name = xpath.section(s,0,0,QString::SectionSkipEmpty);
//     qDebug(QString("xpath, current tag: %1").arg(tag_name));
    xpath = xpath.section(s,1,0xffffffff,QString::SectionSkipEmpty);
    Q_ASSERT(!tag_name.isEmpty());
    element = this->xmlGetElement(element,tag_name,existed);
    Q_ASSERT(!element.isNull());
  }
  //well... if I am correct we should have the correct element now
  return element;
}

void GAgent::xmlRemoveNodeContent(QDomElement node)
{
  if (node.isNull())
    return;
  
  while (node.hasChildNodes())
  {
    node.removeChild(node.firstChild());
  }
}

bool GAgent::xmlGetFlag(QDomElement element, bool& ok)
{
  if (xmlGetInteger(element,ok)==1)
    return true;
  else
    return false;
}

bool GAgent::xmlGetFlag(QString xpath, bool& ok)
{
  if (xmlGetInteger(xpath,ok)==1)
    return true;
  else
    return false;
}

void GAgent::xmlSetFlag(QDomElement element, bool value, bool& ok)
{
  if (value)
    xmlSetInteger(element,1,ok);
  else
    xmlSetInteger(element,0,ok);
}

void GAgent::xmlSetFlag(QString xpath, bool value, bool& ok)
{
  if (value)
    xmlSetInteger(xpath,1,ok);
  else
    xmlSetInteger(xpath,0,ok);
}

int GAgent::xmlGetInteger(QDomElement element, bool& ok)
{
  return element.text().toInt(&ok);
}

int GAgent::xmlGetInteger(QString xpath, bool& ok)
{
  return xmlGetElement(xpath,ok).text().toInt(&ok);
}

void GAgent::xmlSetInteger(QDomElement element, int value, bool& ok)
{
  if (element.isNull())
  {
    ok=false;
  }
  else
  {
    xmlRemoveNodeContent(element);
    element.appendChild(element.ownerDocument().createTextNode(QString::number(value)));
    ok=true;
  }
}

void GAgent::xmlSetInteger(QString xpath, int value, bool& ok)
{
  xmlSetInteger(xmlGetElement(xpath,ok),value,ok);
}

unsigned long GAgent::xmlGetULongInteger(QDomElement element, bool& ok)
{
  return element.text().toULong(&ok);
}

unsigned long GAgent::xmlGetULongInteger(QString xpath, bool& ok)
{
  return xmlGetElement(xpath,ok).text().toULong(&ok);
}

void GAgent::xmlSetULongInteger(QDomElement element, unsigned long value, bool& ok)
{
  if (element.isNull())
  {
    ok=false;
  }
  else
  {
    xmlRemoveNodeContent(element);
    element.appendChild(element.ownerDocument().createTextNode(QString::number(value)));
    ok=true;
  }
}

void GAgent::xmlSetULongInteger(QString xpath, unsigned long value, bool& ok)
{
  xmlSetULongInteger(xmlGetElement(xpath,ok),value,ok);
}

double GAgent::xmlGetDouble(QDomElement element, bool& ok)
{
//   qDebug(QString("xmlGetDouble(): element.text() == %1").arg(element.text()));
  return element.text().toDouble(&ok);
}

double GAgent::xmlGetDouble(QString xpath, bool& ok)
{
  return xmlGetDouble(xmlGetElement(xpath,ok),ok);
}

void GAgent::xmlSetDouble(QDomElement element, double value, bool& ok)
{
  if (element.isNull())
  {
    ok=false;
  }
  else
  {
    
    xmlRemoveNodeContent(element);
    element.appendChild(element.ownerDocument().createTextNode(QString::number(value)));
    ok=true;
  }
}

void GAgent::xmlSetDouble(QString xpath, double value, bool& ok)
{
  xmlSetDouble(xmlGetElement(xpath,ok),value,ok);
}

GVector3 GAgent::xmlGetVector3(QDomElement element, bool& ok)
{
//   qDebug(QString("before get vector3: ok is %1").arg(ok));
  GVector3 v;
  QDomElement e = xmlGetElement(element,"x",ok);
  if (!ok)
    return v.reset();
  v.x = e.text().toDouble(&ok);
  if (!ok)
    return v.reset();
  e = xmlGetElement(element,"y",ok);
  if (!ok)
    return v.reset();
  v.y = e.text().toDouble(&ok);
  if (!ok)
    return v.reset();
  e = xmlGetElement(element,"z",ok);
  if (!ok)
    return v.reset();
  v.z = e.text().toDouble(&ok);
//   qDebug(QString("after get vector3: ok is %1").arg(ok));
  
  if (!ok)
    v.reset();
  return v;
}

GVector3 GAgent::xmlGetVector3(QString xpath, bool& ok)
{
  QDomElement e = xmlGetElement(xpath,ok);
  
  if (!ok)
    return GVector3(0,0,0);
  
  return xmlGetVector3(e,ok);
}

void GAgent::xmlSetVector3(QDomElement element, const GVector3& value, bool& ok)
{
  xmlSetDouble(xmlGetElement(element,"x",ok),value.x,ok);
  xmlSetDouble(xmlGetElement(element,"y",ok),value.y,ok);
  xmlSetDouble(xmlGetElement(element,"z",ok),value.z,ok);
}

void GAgent::xmlSetVector3(QString xpath, const GVector3& value, bool& ok)
{
  QDomElement e = xmlGetElement(xpath,ok);
  xmlSetVector3(e,value,ok);
}

//END Helper member functions for working with ElementData

// SLOTS:

void GAgent::beginPark()
{
//   qDebug("parking agent");
  this->shutdown = TRUE;
}

void GAgent::threadStart(double seconds_elapsed)
{
  if (!this->running())
  {
//     qDebug("starting agent with delta time of " + QString::number(seconds_elapsed));
    this->start();
  }
  else
  {
    qWarning("Not starting agent because it is already running!");
  }
}

void GAgent::receiveInfluence(const GCS::GElementInfluence& influence)
{
}

void GAgent::reparented(const GCS::GElementID& old_parent, const GCS::GElementID& new_parent, const GCS::GMatrix44& transformation)
{
}

// public methods:

bool GAgent::isParked() const
{
  return !running();
}

}


syntax highlighted by Code2HTML, v. 0.9.1