/***************************************************************************
 *   Copyright (C) 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 "GDynamicGeneratorAgent.h"

#include "GSolarsystemCategory.h"
#include "GPlanetCategory.h"

#include <GObject.h>
#include <GForm.h>
#include <GVector3.h>

#include <GWorldData.h>

#include <qdom.h>

#include <exception>

#include <cmath>

using namespace GCS;
using namespace GBE::Util;

namespace GBE {

// GDynamicGeneratorOctreeNode CLASS DECLARATION

/**
 * \class GDynamicGeneratorOctreeNode GDynamicGeneratorAgent.cpp
 * \brief The generation space is structured with octrees
 * @author Raphael Langerhorst
 * Basically a node serves three purposes:
 * 1) It serves as starting point for generating all child nodes in the nodes area,
 * 2) it stores the seed value for the pseudo RNG to generate the child nodes and
 * 3) it stores the size of the area in which a child element would potentially be created.
 */
class GDynamicGeneratorOctreeNode
{
  protected:

    /**
     * Each octreee node directly has access to its data in the
     * element data document. This makes fast updates easily possible.
     * In particular the element data can be kept in sync with the
     * octree with low overhead.
     */
    QDomElement Data;
  
    /**
     * The RNG seed number for this node.
     * THIS is the actual data we need for child generation
     * at the node's position.
     */
    unsigned long RandomSeed;
    
    /**
     * The position relative to the element centre.
     * @todo it should somehow be possible to respond to radius changes of the element's form.
     */
    GCS::GVector3 Position;
    
    /**
     * Segment size of the enclosing cube.
     * This value is both required to generate positions and sizes of
     * child nodes as well as to be able to "randomly" place the
     * created child element somewhere in this cube. If this second
     * functionality were not given, a randomly created element content
     * (=children of one specific element) could look like a grid.
     * @see Position
     */
    double SegmentSize;
    
    /**
     * Set to true when this node is an end node and the element for this
     * node has been created.
     * If this node is not an end node but all child nodes have been used
     * for creating elements then this is also set to true.
     * 
     * This means that if Generated is set to true then the whole cubic
     * area this node covers had been generated. Thus it is possible to
     * reduce nodes and save memory. In the end, when the whole element
     * was created, there will be only one node left: the top node,
     * and its Generated attribute will be set to true.
     */
    bool Generated;
  
    /**
     * Array of child nodes. Seen FROM positive y:
     * the first four are the first four (mathematical) quadrants
     * and the second four children are the last four quadrants.
     */
    GDynamicGeneratorOctreeNode* Children[8];
        
  public:
    /**
     * Constructor.
     */
    GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size);
    
    /**
     * Destructor, deletes all child nodes.
     */
    ~GDynamicGeneratorOctreeNode();
    
    /**
     * Reads the random seed from the element data.
     * The random seed is stored in the rs attribute.
     * Additionally all child nodes are initialized
     * from element data as well.
     * 
     * This is the usual procedure when reinitializing
     * an element that is opend - or woken - from the
     * persistent storage.
     */
    void initFromData();
    
    /**
     * @Returns the seed for the RNG for this node.
     */
    unsigned long getRandomSeed();
    
    /**
     * @Returns the position of this node.
     */
    GCS::GVector3 getPosition();
    
    /**
     * @Returns the segment size of the enclosing node cube.
     */
    double getSegmentSize();
    
    /**
     * @Returns true when given position is inside the cube of this octree node.
     */
    bool isPositionInsideNode(const GCS::GVector3& position);
    
    /**
     * @Returns true when given area is enclosed by the cube of this octree node.
     */
    bool isAreaInsideNode(const GVector3& position, double radius);
    
    /**
     * @Returns true when given area touches the cube of this octree node.
     */
    bool touchesArea(const GCS::GVector3& position, double radius);
    
    /**
     * Sets Generated to true.
     * @see Generated
     */
    void setGenerated();
    
    /**
     * @Returns true when an element was already created at this node position.
     * @see Generated
     */
    bool isGenerated();
    
    /**
     * @Returns a pointer list with all child nodes.
     */
    QPtrList<GDynamicGeneratorOctreeNode> getChildNodes();
    
    
    /**
     * @returns true when child nodes are created (so they are in memory).
     */
    bool isNodeExpanded();
    
    /**
     * Creates all 8 child nodes for this octree node.
     *
     * @param rng specifies the random number generator to use.
     * @return true when all child nodes have been successfully created.
     * @return false on out of memory errors.
     */
    bool expandNode(Util::PseudoRNG& rng);
    
    /**
     * Deletes all child nodes.
     */
    void reduceNode();
    
    /**
     * Reduces all child nodes that have been used for element creation (isGenerated()==true)
     */
    void reduceGenerated(bool recursive = true);
    
    /**
     * @Returns the child node of this octree node that covers given position.
     * @Returns NULL if position is completely outside of this node!
     * @todo write a test case for this.
     */
    GDynamicGeneratorOctreeNode* getChildNodeForPosition(const GCS::GVector3& position);
    
    /**
     * @Returns all child nodes that cover part of given area;
     * @todo write a test case for this.
     */
    QPtrList<GDynamicGeneratorOctreeNode> getChildNodesForArea(const GCS::GVector3& position, double radius);
    
    /**
     * @returns a deterministic random position based on RandomSeed inside this node's cube.
     */
    GCS::GVector3 getRandomPositionInCube(Util::PseudoRNG& rng);
};


// GDynamicGeneratorOctreeNode IMPLEMENTATION

GDynamicGeneratorOctreeNode::GDynamicGeneratorOctreeNode(QDomElement data, unsigned long rand_seed, const GCS::GVector3& position, double segment_size)
: Data(data),
  RandomSeed(rand_seed),
  Position(position),
  SegmentSize(segment_size),
  Generated(false)
{
  Children[0]=NULL;
  Children[1]=NULL;
  Children[2]=NULL;
  Children[3]=NULL;
  Children[4]=NULL;
  Children[5]=NULL;
  Children[6]=NULL;
  Children[7]=NULL;
  
  if (data.attribute("done","0")=="1")
  {
    setGenerated();
  }
}

GDynamicGeneratorOctreeNode::~GDynamicGeneratorOctreeNode()
{
  if (this->isNodeExpanded())
    this->reduceNode();
}

unsigned long GDynamicGeneratorOctreeNode::getRandomSeed()
{
  return RandomSeed;
}

GVector3 GDynamicGeneratorOctreeNode::getPosition()
{
  return Position;
}

double GDynamicGeneratorOctreeNode::getSegmentSize()
{
  return SegmentSize;
}

bool GDynamicGeneratorOctreeNode::isPositionInsideNode(const GVector3& position)
{
  double half_seg_size = this->SegmentSize*0.5;
  
  if (position.x >= this->Position.x - half_seg_size && position.x <= this->Position.x + half_seg_size &&
      position.y >= this->Position.y - half_seg_size && position.y <= this->Position.y + half_seg_size &&
      position.z >= this->Position.z - half_seg_size && position.z <= this->Position.z + half_seg_size)
    return true;
    
  else
    return false;
}

bool GDynamicGeneratorOctreeNode::isAreaInsideNode(const GVector3& position, double radius)
{
  double half_seg_size = this->SegmentSize*0.5;
  
  if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size &&
      position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size &&
      position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size)
    return true;
    
  else
    return false;
}

bool GDynamicGeneratorOctreeNode::touchesArea(const GVector3& position, double radius)
{

  if (this->isPositionInsideNode(position))
    return true;

  //@todo this testing testes spheres!!! but a node is a cube!
  if (this->Position.distanceTo(position) < radius + this->SegmentSize*0.8)
    return true;
  
  return false;
  
    
//   
//   
//   if (position.x + radius >= this->Position.x - half_seg_size && position.x - radius <= this->Position.x + half_seg_size ||
//       position.y + radius >= this->Position.y - half_seg_size && position.y - radius <= this->Position.y + half_seg_size ||
//       position.z + radius >= this->Position.z - half_seg_size && position.z - radius <= this->Position.z + half_seg_size)
//     return true;
//     
//   else
//     return false;
}



void GDynamicGeneratorOctreeNode::setGenerated()
{
  Data.setAttribute("done","1");
  this->Generated = true;
}

bool GDynamicGeneratorOctreeNode::isGenerated()
{
  return Generated;
}

QPtrList<GDynamicGeneratorOctreeNode> GDynamicGeneratorOctreeNode::getChildNodes()
{
  QPtrList<GDynamicGeneratorOctreeNode> list;
  for (short i = 0; i< 8; i++)
  {
    list.append(Children[i]);
  }
  return list;
}


bool GDynamicGeneratorOctreeNode::isNodeExpanded()
{
//   if (Children[0]==NULL)
//     return false;
//   else
//     return true;
  return Children[0] ? true : false;
}

bool GDynamicGeneratorOctreeNode::expandNode(PseudoRNG& rng)
{
  if (isGenerated())
  {
    qWarning("Node is expanding ALTOUGH it is already generated! This is madness! - Please contact the developers!");
  }
  
  if (!isNodeExpanded())
  {
    //@todo 1) set RNG, 2) create 1 through 8 with correct positions and segment sizes
    rng.setNumber(this->RandomSeed);
    
    double child_segment_size = this->SegmentSize*0.5;
    
    GVector3 child_pos_array[8];
    
    GVector3 child_pos(this->Position.x + child_segment_size*0.5,
                       this->Position.y + child_segment_size*0.5,
                       this->Position.z + child_segment_size*0.5);                   
                       
    // BEGIN TOPLEVEL NODES
    child_pos_array[0] = child_pos;
    child_pos.x -= child_segment_size;
    child_pos_array[1] = child_pos;
    child_pos.z -= child_segment_size;
    child_pos_array[2] = child_pos;
    child_pos.x += child_segment_size;
    child_pos_array[3] = child_pos;
    // END TOPLEVEL NODES
    // BEGIN BOTTOMLEVEL NODES
    child_pos.y -= child_segment_size;
    child_pos.z += child_segment_size;
    child_pos_array[4] = child_pos;
    child_pos.x -= child_segment_size;
    child_pos_array[5] = child_pos;
    child_pos.z -= child_segment_size;
    child_pos_array[6] = child_pos;
    child_pos.x += child_segment_size;
    child_pos_array[7] = child_pos;
    // END BOTTOMLEVEL NODES
    
    QDomElement gs[8];
    
    if (Data.hasChildNodes())
    {
      QDomNodeList list = Data.childNodes();
      if (list.count()==8)
      {
        int i;
        for (i = 0; i<8; i++)
        {
          QDomElement e = list.item(i).toElement();
          Q_ASSERT(!gs[i].isNull());
          bool ok;
          int n = e.attribute("n",QString::number(i)).toInt(&ok);
          if (!ok)
            n = i;
          gs[n] = e;
        }
        //let's do a sanity check
        bool consistent = true;
        for (i = 0; i<8; i++)
        {
          if (gs[i].isNull())
          {
            qWarning("INCONSISTENCY DETECTED: found existing octree node data, but not all child elements could be found! Replacing existing node data.");
            consistent = false;
          }
        }
        if (!consistent)
        {
          for (i = 0; i<8; i++)
          {
            if (!gs[i].isNull())
            {
              gs[i].clear();
            }
          }
        }
      }
      else
      {
        qWarning(QString("INCONSISTENCY DETECTED: octree nodes for world generation must either have 0 or 8 child nodes! We have %1. Replacing existing nodes").arg(list.count()));
        while(Data.hasChildNodes())
        {
          qWarning("clearing node");
          Data.firstChild().clear();
        }
      }
    }
    
    if (!Data.hasChildNodes())
    {
      //if there is nothing we create the child nodes now.
      for (int i = 0; i<8; i++)
      {
        QDomElement e = Data.ownerDocument().createElement("gs");
        Data.appendChild(e);
        e.setAttribute("n",QString::number(i));
        int done = this->isGenerated() ? 1 : 0;
        e.setAttribute("done",QString::number(done));  // in case we're expanding although we are already generated...
        gs[i] = e;
      }
    }
    
    for (short child_number = 0; child_number < 8; child_number++)
    {
      unsigned long child_random_seed = rng.getNumberInt();
      Children[child_number] = new GDynamicGeneratorOctreeNode(gs[child_number],child_random_seed,child_pos_array[child_number],child_segment_size);
      if (Children[child_number]==NULL)
      {
        for (short i=0; i<child_number; i++)
        {
          delete Children[i];
          Children[i]=NULL;
          gs[i].clear();
        }
        qWarning("Octree child node creation: out of memory");
        return false;
      }
    }
  }
  return true; //if nodes either already exist or have been successfully created.
}

void GDynamicGeneratorOctreeNode::reduceNode()
{
  //We use Children[0] to test whether expanding had occured or not.
  if (isNodeExpanded())
  {
    for (short i = 0; i<8; i++)
    {
      delete Children[i];
      Children[i]=NULL;
    }
    Data.clear();
  }
}

void GDynamicGeneratorOctreeNode::reduceGenerated(bool recursive)
{
  if (isNodeExpanded())
  {
    bool all_childs_generated = true;
    if (recursive)
    {
      for (short i = 0; i<8; i++)
      {
        Children[i]->reduceGenerated(recursive);
      }
    }
    for (short i = 0; i<8; i++)
    {
      if (Children[i]->isGenerated() == false)
      {
        all_childs_generated = false;
      }
    }
    if (all_childs_generated)
    {
      this->reduceNode();
      this->setGenerated();
    }
  }
}

GDynamicGeneratorOctreeNode* GDynamicGeneratorOctreeNode::getChildNodeForPosition(const GVector3& position)
{
  if (!this->isNodeExpanded())
  {
    qWarning("Node not expanded, can't return children");
    return NULL;
  }
  
  if (this->isPositionInsideNode(position))
  {
  
    for (short i = 0; i<8; i++)
    {
      if (Children[i]->isPositionInsideNode(position))
        return Children[i];
    }
    qWarning("Position was inside node but no appropriate child node found!");
    return NULL;
  }
  else
    return NULL;
}

QPtrList<GDynamicGeneratorOctreeNode> GDynamicGeneratorOctreeNode::getChildNodesForArea(const GVector3& position, double radius)
{
  QPtrList<GDynamicGeneratorOctreeNode> list;
  
  if (!this->isNodeExpanded())
  {
    qWarning("Node is not expanded, can't get children for area!!");
    return list;
  }
  
//   qDebug("Getting child nodes in area");
  
  for (short i = 0; i<8; i++)
  {
    if (Children[i]->touchesArea(position,radius))
    {
//       qDebug("Found child in area");
      list.append(Children[i]);
    }
  }
  
  return list;
}

GVector3 GDynamicGeneratorOctreeNode::getRandomPositionInCube(PseudoRNG& rng)
{
  rng.setNumber(this->RandomSeed+10); //pick some other seed value than for child nodes.
  GVector3 pos(this->Position.x + (rng.getNumberDouble()-0.5)*this->SegmentSize,
               this->Position.y + (rng.getNumberDouble()-0.5)*this->SegmentSize,
               this->Position.z + (rng.getNumberDouble()-0.5)*this->SegmentSize);
               
  return pos;
}

// GDynamicGeneratorAgent IMPLEMENTATION


GDynamicGeneratorAgent::GDynamicGeneratorAgent()
: EnergyFractionChildCreation(0),
  EnergyFractionDirtyFlag(true),
  DataLoaded(FALSE),
  RandomSeed(1000),
  Categories(),
  CreationTime(QDateTime::currentDateTime()),
  Density(5),
  CreateAllAtOnce(false),
  TopNode(NULL)
{
  this->Categories.setAutoDelete(true);
}


GDynamicGeneratorAgent::~GDynamicGeneratorAgent()
{
  if (TopNode)
  {
    delete TopNode;  //deletes all child nodes recursively.
    TopNode=NULL;
  }
}

void GDynamicGeneratorAgent::generateInArea(const GCS::GVector3& position, double radius)
{
  //implement recursive search until smallest node is found that still encompasses the whole area - then generate this node (might STILL hold childnodes that do the actual generation!!)
  
//   qDebug("Generating child elements in area");
  
  GForm* f = requestForm();
  
  if (f->getRadiusMax() < position.length() - radius)
  {
//     qDebug("Area completely outside of this element");
    return; //if target area is completely outside of this element's form  
  }
  
  //generate top node if it does not exist
  if (TopNode == NULL)
  {
//     qDebug("TopNode did not exist, generating top node...");
    
    
    RNG.setNumber(this->RandomSeed);
    
    unsigned long rs = RNG.getNumberInt();
    
    bool ok;
    QDomElement data = xmlGetElement("/worldgeneration/gs",ok);
    Q_ASSERT(!data.isNull());
    if (!data.hasAttribute("n"))
      data.setAttribute("n","0");
    if (!data.hasAttribute("done"))
      data.setAttribute("done","0");
    
    if (data.hasAttribute("rs"))
    {
      bool ok;
      unsigned long rs_ = data.attribute("rs",QString::number(rs)).toULong(&ok);
      if (ok)
        rs = rs_;
    }
    else
    {
      data.setAttribute("rs",QString::number(rs));
    }
    
    TopNode = new GDynamicGeneratorOctreeNode(data,rs,GVector3(0,0,0),f->getRadiusMax()*2);
    if (TopNode==NULL)
    {
      qWarning("not enough memory to generate top node");
      return;
    }
    TopNode->expandNode(RNG);
  }
  
  if (TopNode->isGenerated())
  {
//     qDebug("TopNode is generated, thus the whole element is already generated");
    return;  //Whole element is generated.
  }
  
  //here we store the current depth of the tree, when depth == density()
  //then we are at the lowest level - here actual element generation happens
  short unsigned depth = 0;
  
  //start recursive search until the smallest node is found that still covers the whole given area
  recursiveGeneration(TopNode,position,radius,depth);  
}

void GDynamicGeneratorAgent::recursiveGeneration(GDynamicGeneratorOctreeNode* node, const GCS::GVector3& position, double radius, short unsigned depth)
{
//   qDebug("Recursive generation, depth = " + QString::number(depth));
  if (node->isGenerated()) //if node is already generated there is nothing to do, note that we currently double check this
  {
//     qDebug("Node is generated, returning");
    return;
  }
  
  if (depth>=this->Density)
  {
    //@todo determine category - if a category is determined (and not empty space) then we generate with that category at this node
    
    //mark node as already being used to generate elements
    node->setGenerated();
    
    RNG.setNumber(node->getRandomSeed());
    double random_number = RNG.getNumberDouble();
    
//     qDebug("Double number generated: " + QString::number(random_number));
    
    double range_sum = 0;
    
    for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next())
    {
      range_sum += category->getRange();
      if (range_sum > 1)
        qWarning("Range sum bigger than 1: " + QString::number(range_sum));
      if (range_sum >= random_number)  //category HIT, we generate a child element!!
      {
//         qDebug("Category found, creating element");
        GVector3 position = node->getRandomPositionInCube(RNG);
        
        GElement* element = this->createElement(*category,position);
        
        Q_CHECK_PTR(element);
        
        if (element)
        {
          emit this->childElementCreated(element);
          
          element->executeElement(this->CreationTime.secsTo(QDateTime::currentDateTime()));
          
        }
        
        return;
      }
    }
    //if we get here then no generation happened
    //that means no category applied as the sum of category ranges
    //did not exceed the generated random number.
  }
  else
  {
    if (!node->isNodeExpanded())
    {
      node->expandNode(RNG);
    }
    
    if (node->isNodeExpanded())  //we check again in case node can't be expanded (memory!!)
    {
      unsigned short new_depth = depth+1;
      QPtrList<GDynamicGeneratorOctreeNode> nodes = node->getChildNodesForArea(position, radius);
      for (GDynamicGeneratorOctreeNode* current = nodes.first(); current != NULL; current = current = nodes.next())
      {
        if (!current->isGenerated())
        {
          recursiveGeneration(current,position,radius,new_depth);
          
          //reduce generated nodes, saves lots of memory.
          //this means that if a whole (higher level) node had been
          //completely generated then all child nodes can be removed and this higher node can be set to generated
          //does not need to be recursive, as we call it at each level anyway...
          current->reduceGenerated(false);
        }
      }
    }
  }
}

GElement* GDynamicGeneratorAgent::createElement(const GDynamicGeneratorCategory& category, const GCS::GVector3& position)
{
  //hmm.... how to get deterministic energy? I think THIS will be _different_ for each generation...
  // at least if the element is not generated in "all at once" mode.
  //@todo use a member variable for storing the fraction of energy we take, this takes too much cpu time
  if (this->EnergyFractionDirtyFlag)
  {
    double range_sum = 0;
    for (GDynamicGeneratorCategory* category = Categories.first(); category != NULL; category = Categories.next())
    {
      range_sum += category->getRange();
    }
    if (range_sum <= 0)
    {
      qWarning("Generating child elements will not work: range sum is 0 or less");
      this->EnergyFractionChildCreation=0;
    }
    else
    {
      this->EnergyFractionChildCreation = 1/(std::pow((double)8,(double)this->Density)*range_sum);
    }
    this->EnergyFractionDirtyFlag=false;
  }
  GEnergy* e = new GEnergy(requestEnergy()->take(this->EnergyFractionChildCreation));
  double level_shift = (e->sigma()*(RNG.getNumberDouble()-0.5));
  double level_shift_positive = level_shift;
  if (level_shift < 0)
    level_shift_positive *= -1;
  e->set(e->level()+level_shift,e->amount(),e->sigma()-level_shift_positive);

  //@todo this energy change *should* be reflected in the original energy,
  //  but if it's used for all child elements it probably doesn't matter much
  
  Q_CHECK_PTR(e);
  
  if (e==NULL)
    return NULL;
  
  //@todo check if there is already a child element that covers this area if chosen category does not allow overlapping
  //@todo how to set parent ID for created element? Additional parameter?
  
  GForm* form = category.createForm(*e,position,RNG);
  
  Q_CHECK_PTR(form);
  
  if (form==NULL)
  {
    delete e;
    return NULL;
  }
  
  GObject* object = new GObject(e,form,getElementID(),GElementID::getFreeID(),GElementID(0),new QDomDocument(),requestObject()->getWorldData());
  Q_CHECK_PTR(object);
  
  GElement* element = new GElement(object);
  Q_CHECK_PTR(element);
  
  category.postProcess(element,RNG);  // DO NOT(!!!) execute agents in postprocessing
  
  return element;
}

void GDynamicGeneratorAgent::loadData(bool force)
{
  if (force)
    this->DataLoaded = false;

  if (this->DataLoaded)
    return;

  bool ok;
  unsigned long rs = xmlGetULongInteger("/worldgeneration/randomseed",ok);
  if (ok)
    this->RandomSeed = rs;
  else
  {
    qWarning("Random Seed was not found!");
    initRandomSeed(RandomSeed);
  }
  
  QDateTime time;
  time.fromString(xmlGetElement("/worldgeneration/creationtime",ok).text());
  if (ok)
    this->CreationTime = time;
  else
  {
    qWarning("Creation Time was not found!");
    initCreationTime(CreationTime);
  }
  
  int density = xmlGetInteger("/worldgeneration/density",ok);
  if (density < 0)
  {
    qWarning("Density < 0; setting density to 3");
    density = 3;
    ok=false;
  }
  if (ok)
    this->Density = density;
  else
  {
    qWarning("Density was not found!");
    initDensity(Density);
  }
  
  int all_at_once = xmlGetInteger("/worldgeneration/allatonce",ok);
  if (ok)
  {
    if (all_at_once == 1)
      this->CreateAllAtOnce = true;
    else
      this->CreateAllAtOnce = false;
  }
  else
  {
    qWarning("CreateAllAtOnce was not found!");
    initAllAtOnce(this->CreateAllAtOnce);
  }
  
  bool existed;
  QDomElement categories_element = xmlGetElement("/worldgeneration/categories",existed);
  if (!categories_element.hasChildNodes())
  {
    qWarning("No categories found in element data, this agent is quite useless without these.");
    if (!this->Categories.isEmpty())
    {
      
    }
  }
  else
  {
    this->Categories.clear();  //autodelete = true
    QDomNodeList categories = categories_element.childNodes();
    for (int i=0; i<categories.count(); i++)
    {
      QDomElement c = categories.item(i).toElement();
      if (c.isNull())
      {
        qWarning("Category is null element!");
      }
      else
      {
        QString name = c.tagName();
        GDynamicGeneratorCategory* c_object = NULL;
        if (name == "solarsystem")
        {
          bool existed,ok;
          double range = xmlGetElement(c,"range",existed).text().toDouble(&ok);
          if (existed && ok)
          {
            c_object = new GSolarsystemCategory(range);
          }
          else if (existed && !ok)
          {
            qWarning(QString("Could not convert number: %1").arg(xmlGetElement(c,"range",existed).text()));
          }
          else
          {
            qWarning("Range element not found for solarsystem category!");
          }
        }
        else if (name == "planet")
        {
          bool existed;
          bool ok;
          double range = xmlGetElement(c,"range",existed).text().toDouble(&ok);
          if (existed && ok)
          {
            c_object = new GPlanetCategory(range);
          }
          else if (existed && !ok)
          {
            qWarning(QString("Could not convert number: %1").arg(xmlGetElement(c,"range",existed).text()));
          }
          else
          {
            qWarning("Range element not found for planet category!");
          }
        }
        else
        {
          qWarning(QString("Unknown category name: %1").arg(name));
        }
        
        if (c_object)
        {
          this->Categories.append(c_object);
        }
      }
    }
  }
  
  this->DataLoaded = true;
}

void GDynamicGeneratorAgent::receiveInfluence(const GCS::GElementInfluence& influence)
{
  if (this->requestObject()->hasForm())
  {
    if (influence.source() == this->requestObject()->getParent() || influence.source() == this->getElementID())
    {
//       qDebug("Recieved influence from myself or parent, not creating content");
      return;  //don't react to parent influence or own influence, since that would automatically create ALL the content
    }
    
//     qDebug("Generating element content");
    
    if (!this->DataLoaded)
      loadData();  //make sure we've all data initialized
    
    //the criteria on which we create child elements is based on whether the
    //influence comes from an external source (= a sibling and neither the parent,
    //nor any of the existing children)
    
    const GElement* sender = this->requestObject()->getWorldData()->read(influence.source());
    
    Q_CHECK_PTR(sender);
    
    if (sender==NULL)
      return;
    
    const GObject* sender_object = sender->getObject();
    
    Q_CHECK_PTR(sender_object);
    
    if (!sender_object->hasForm())
      return;
    
    const GForm* sender_form = sender_object->getForm();
    
    Q_CHECK_PTR(sender_form);
    
    //@todo continue from here
    
//     GActiveElementInfluence& active_element_influence = dynamic_cast<GActiveElementInfluence&>(influence);
    
//     qDebug("Received element influence, checking if additional elements should be created.");
    
    GVector3 pos = sender_form->Position;
    GVector3 rot = sender_form->Rotation;
    //@todo add a factor to element data
    double arearadius = sender_form->getRadiusMax();
    double detail_arearadius = sender_form->getRadiusMin();
    
    // Generating contents for active elements INSIDE this element. This would
    // mean for example to see the planets within a solar system when the
    // source element is inside the solar system itself.
    // Note that for this generation the normal area size is used as
    // the source element moves inside this element.
    if (sender_object->getParent() == this->getElementID())
    {
//      qDebug("Influence source is a child element");
      //no transformation needed
      generateInArea(pos,arearadius);
    }
    
    // Generating contents for active elements OUTSIDE this element. This would
    // mean for example to see the planets within a solar system even when being
    // far away from the solar system itself.
    // Note that for this generation the detail area radius is used as the source
    // element is not really inside this element. But it can be useful to see
    // element content if the element is getting nearer.
    else if (sender_object->getParent() == this->requestObject()->getParent() /* && active_element_influence.parent() != this->getElementID()*/ )
    {
//      qDebug("Influence source on the same hierarchy level");
      GForm* f = requestForm();
      //transformation is just influence source position - this position, and rotation!!
      pos.sub(f->Position);
      rot.sub(f->Rotation);
      
      pos.turnAroundAxis(GVector3(1,0,0),rot.x);
      pos.turnAroundAxis(GVector3(0,1,0),rot.y);
      pos.turnAroundAxis(GVector3(0,0,1),rot.x);
      
      generateInArea(pos,detail_arearadius);
    }
  }
}

//BEGIN XML Element Data initialization

/*
* <worldgeneration>
*   <randomseed>number</randomseed>
*   <creationtime>datetime</creationtime>
*   <density>double value</density>
*   <allatonce>bool</allatonce>
*   <categories>
*     <!-- sequence of categories, these are examples -->
*     <solarsystem>
*       <range>double value</range>
*       <radiusmin>double value</radiusmin>
*       <radiusmax>double value</radiusmax>
*       <allowoverlap>bool</allowoverlap>
*     </solarsystem>
*     <planet>
*       <range>double value</range>
*       <radiusmin>double value</radiusmin>
*       <radiusmax>double value</radiusmax>
*       <allowoverlap>bool</allowoverlap>
*     </planet>
*   </categories>
* </worldgeneration>
*/

void GDynamicGeneratorAgent::initRandomSeed(unsigned long random_seed)
{
  this->RandomSeed = random_seed;
  bool existed;
  xmlSetULongInteger("/worldgeneration/randomseed",random_seed,existed);
}

void GDynamicGeneratorAgent::initCreationTime(QDateTime creation_time)
{
  this->CreationTime = creation_time;
  bool existed;
  QDomElement e = xmlGetElement("/worldgeneration/creationtime",existed);
  if (e.isNull())
  {
    qWarning("Can't set creation time because element is NULL!");
    return;
  }
  xmlRemoveNodeContent(e);
  e.appendChild(e.ownerDocument().createTextNode(creation_time.toString(Qt::ISODate)));
}

void GDynamicGeneratorAgent::initDensity(unsigned short density)
{
  this->Density = density;
  this->EnergyFractionDirtyFlag = true;
  bool existed;
  xmlSetInteger("/worldgeneration/density",density,existed);
}

void GDynamicGeneratorAgent::initAllAtOnce(bool create_all_at_once)
{
  this->CreateAllAtOnce = create_all_at_once;
  bool existed;
  int n = create_all_at_once==true ? 1 : 0;
//   qDebug(QString("all at once creation is set to %1").arg(n));
  xmlSetInteger("/worldgeneration/allatonce",n,existed);
}

//@todo this is currently a bit... well, we should think of some kind of factory or plugin system for categories
void GDynamicGeneratorAgent::initCategory(GDynamicGeneratorCategory* category)
{
  this->Categories.append(category);
  this->EnergyFractionDirtyFlag = true; //recalculate energy fraction for child creation
  QString classname = "category";

  try
  {
    GSolarsystemCategory* c = dynamic_cast<GSolarsystemCategory*>(category);
    classname = "solarsystem";
  }
  catch(std::exception e)
  {
    try
    {
      GPlanetCategory* c = dynamic_cast<GPlanetCategory*>(category);
      classname = "planet";
    }
    catch(std::exception e)
    {
    }
  }

  if (classname=="category")
  {
    qWarning("unknown category, can't add it to element data");
    return;
  }

  bool existed;
  xmlSetDouble(QString("/worldgeneration/") + classname + "/range",category->getRange(),existed);

  //radiusmin, radiusmax, allowoverlap are not used for now... solar systems and planets both don't need them  
}

//END XML Element Data initialization

};


syntax highlighted by Code2HTML, v. 0.9.1