/***************************************************************************
 *   Copyright (C) 2003 - 2004 Martin Reinsprecht                          *
 *   rema10@hyperbox.org                                                   *
 *   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.                                       *
 ***************************************************************************/

#ifndef GBEGDYNAMICGENERATORAGENTH
#define GBEGDYNAMICGENERATORAGENTH

#include "PseudoRNG.h"

#include "GDynamicGeneratorCategory.h"

#include <GElementInfluence.h>
#include <GAgent.h>
#include <GElement.h>
#include <GEnergy.h>
#include <GVector3.h>

#include <qdatetime.h>
#include <qptrlist.h>

namespace GBE
{


//this is defined in the implementation file as it is specifically created for this agent
class GDynamicGeneratorOctreeNode;


/**
  \class GDynamicGeneratorAgent GDynamicGeneratorAgent.h
  \brief Implements deterministic random element content generation.
  @author Martin Reinsprecht (idea), Raphael Langerhorst (implementation)

  Dynamic world generation is a key concept in creating huge worlds.
  This class serves as base class for content creating agents for
  specific elements.

  A random number generator (RNG) is used to create a series
  of random numbers which are mapped to coordinates octree
  inside the element. According to a mapping it is possible to
  tell what kind of child element is created at a certain location.
  This child element is given a seed value for its own RNG
  according to the current RNG state. Categories are used
  to create the child elements. The correct category is chosen
  by the random number.

  This mechanism results in a random but deterministic
  universe according to the seed value for the first element.
    
  Good examples for using this agent are galaxies, solar systems, planet
  surface generation, ...
  
  @note This agent can only work if the element has a form attribute!
  
  @todo Make this Agent thread safe - especially receiveInfluence!! Or put generation into a separate thread.
  
  @todo The energy used for creation is NOT deterministic as we GEnergy::take() some out of the current element(!)
 *
 * Unlike other more simple agents this agent does not directly read its data
 * from the element data, but keeps a copy in this class to reduce overhead.
 * 
 * Adds the following xml data to the element data:
 * <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>
 *     </solarsystem>
 *     <planet>
 *       <range>double value</range>
 * <!-- these are possible additional values (generic category)
 *       <radiusmin>double value</radiusmin>
 *       <radiusmax>double value</radiusmax>
 *       <allowoverlap>bool</allowoverlap>
 * -->
 *     </planet>
 *   </categories>
 *   <!-- gs means generation state and stores the complete state of the octree 
 *        if all is generated then the done attribute of the top node is 1,
 *        and then there is almost no memory consumption.
 *        n is the number of the child node so position and everything can be applied.
 *        rs is the random seed for the top node, only the top node needs the seed. -->
 *   <gs n="0" done="0" rs="32152">
 *     <gs n="0" done="1"></gs>
 *     <gs n="1" done="0"></gs>
 *     <gs n="2" done="1"></gs>
 *     <gs n="3" done="0"></gs>
 *        <gs n="0" done="1"></gs>
 *        <gs n="1" done="0"></gs>
 *        <gs n="2" done="1"></gs>
 *        <gs n="3" done="1"></gs>
 *        <gs n="4" done="0"></gs>
 *        <gs n="5" done="0"></gs>
 *        <gs n="6" done="0"></gs>
 *        <gs n="7" done="1"></gs>
 *     <gs n="4" done="1"></gs>
 *     <gs n="5" done="0"></gs>
 *     <gs n="6" done="0"></gs>
 *     <gs n="7" done="0"></gs>
 *   </gs>
 * </worldgeneration>
 */


class GDynamicGeneratorAgent : public GCS::GAgent
{
  Q_OBJECT

  private:

    /**
     * Holds the fraction of energy that should be taken
     * from the own energy to create a child element.
     * It is for the moment auto generated and it's simply
     * better for the performance to have the value here
     * rather than recomputing it for each element.
     */
    double EnergyFractionChildCreation;

    /**
     * Set to true when the above value should be recalculated
     * the next time it is needed.
     */
    bool EnergyFractionDirtyFlag;
    
  protected:

    /**
     * Is true if latest data has been fetched from element data.
     * If it's false, the agent initializes its dat from element data
     * and sets this attribute to true.
     * It is false by default, which means it will initialize from
     * element data.
     * DataLoaded is checked whenever an influence arrives and thus
     * the up-to-date data is needed.
     *
     */
    bool DataLoaded;
  
    /**
     * The seed with which the RNG starts at the initial position.
     */
    unsigned long RandomSeed;
    
    /**
     * Stores all available category implementations, these are used
     * to generate child elements at certain nodes.
     * @note It is VERY important to have a total range size < 1 because
     *       the random value used to determine the category is between
     *       0 and 1. If the random value is bigger than the sum of all
     *       categories then nothing is created at that position.
     * @see recursiveGeneration(), GDynamicGeneratorCategory
     */
    QPtrList<GDynamicGeneratorCategory> Categories;
  
    /**
     * Holds the time at which this element was created.
     * @see GDynamicGeneratorCategory::createElement()
     */
    QDateTime CreationTime;
    
    /**
     * Specifies the density of the octree for element generation.
     * A higher value gives finer grained child creation.
     * @note Every step means at maximum eight times more octree nodes!!
     * High values should be avoided, a density of 5 can already give
     * 32768 octree nodes! Still, the dynamic management of nodes
     * minimises this memory exhaustion.
     */
    unsigned short Density;
    
    /**
     * Determines if all the element content should be
     * created in one go as soon as a user influence is
     * recognised. 
     * This is required if child elements heavily influence
     * each other. Otherwise (like solar systems or galaxies)
     * it can be set to false. Default value is false.
     */
    bool CreateAllAtOnce;
    
    /**
     * The random number generator.
     */
    Util::PseudoRNG RNG;
    
    /**
     * The top node of the octree which is used for content creation.
     */
    GDynamicGeneratorOctreeNode* TopNode;
    
  protected:
  
    /**
     * Generates elements in given sphere area.
     * If active elements are detected by receiveInfluence() then this
     * method is used to do the actual element generation.
     * @see receiveInfluence()
     */
    void generateInArea(const GCS::GVector3& position, double radius);
    
    /**
     * Implements recursive generation of elements in area.
     * When the lowest level is reached (depth==this->Density) then a
     * random value between 0 and 1 is generated with the seed of the node
     * with which the category can be determined.
     * Categories have a range. The Categories list will be iterated through
     * and the range attribute of every category will be added. As
     * soon as the determined random value is reached the currently selected
     * category is used for element creation.
     */
    void recursiveGeneration(GDynamicGeneratorOctreeNode* node, const GCS::GVector3& position, double radius, short unsigned depth);
    
    /**
     * Creates an element at a given position.
     * For special creation routines this method can be reimplemented.
     */
    virtual GCS::GElement* createElement(const GDynamicGeneratorCategory& category, const GCS::GVector3& position);


    /**
     * Loads current data from element data. But only
     * if DataLoaded is false.
     * Sets DataLoaded to true.
     * If force is true then data is loaded from element data no matter
     * what DataLoaded is set to.
     */
    virtual void loadData(bool force = false);
  
  public:
    /**
     * Constructor.
     * You MUST use the init member functions to initialize this agent,
     * or the element data must already contain worldgeneration data.
     */
    GDynamicGeneratorAgent();

    /**
     * Virtual destructor.
     */
    virtual ~GDynamicGeneratorAgent();

    /**
     * Reacts to user influences and creates contents of the element
     * accordingly. That means, if a user is recognised at a certain
     * location this agent will create element content in this area
     * so it is visible and active for the user.
     * @see generateInArea()
     */
    virtual void receiveInfluence(const GCS::GElementInfluence& influence);


    //BEGIN XML Element Data initialization

    //see also loadData()

    /**
     * Initializes given random seed for the pseudo RNG in the element data.
     *
     * @param random_seed The seed with which the RNG starts at the initial position.
     */
    virtual void initRandomSeed(unsigned long random_seed);

    /**
     * @param creation_time Holds the time at which this element was created.
     * @see GDynamicGeneratorCategory::createElement()
     */
    virtual void initCreationTime(QDateTime creation_time = QDateTime::currentDateTime());

    /**
     * @param density Specifies the density of the octree for element generation.
     *                A higher value gives finer grained child creation.
     * @note Every step means at maximum eight times more octree nodes!!
     * High values should be avoided, a density of 5 can already give
     * 32768 octree nodes! Still, the dynamic management of nodes
     * minimises this memory exhaustion.
     */
    virtual void initDensity(unsigned short density);

    /**
     * @param create_all_at_once Determines if all the element content should be
     *                           created in one go as soon as a user influence is
     *                           recognised.
     *                           This is required if child elements heavily influence
     *                           each other. Otherwise (like solar systems or galaxies)
     *                           it can be set to false. Default value is false.
     */
    virtual void initAllAtOnce(bool create_all_at_once);

    /**
     * Adds given category to element data.
     *
     * Categories are used to generate child elements at certain nodes.
     *
     * GDynamicGeneratorAgent takes ownership over given category (for memory management).
     *
     * @note It is VERY important to have a total range size < 1 because
     *       the random value used to determine the category is between
     *       0 and 1. If the random value is bigger than the sum of all
     *       categories then nothing is created at that position.
     * @see recursiveGeneration(), GDynamicGeneratorCategory
     */
    virtual void initCategory(GDynamicGeneratorCategory* category);

    //END XML Element Data initialization
};

}

#endif
