/***************************************************************************
 *   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.                                       *
 ***************************************************************************/

#ifndef GXMLDATACONTROLLER_H
#define GXMLDATACONTROLLER_H

#include "GDataController.h"

#include <qdom.h>
#include <qmap.h>
#include <qvaluelist.h>
#include <qstringlist.h>
#include <GElementID.h>
#include <GElement.h>

namespace GWE
{

class GCoreXmlSerializer;
class GStorage;
class GXmlNetwork;


/**
 * \Class GXmlDataController GXmlDataController.h
 * \brief Implements XML based data management.
 * @author Raphael Langerhorst
 * 
 * The XML Data Controller uses GXmlNetwork based network implementations
 * and GXmlStorage based data storage backends to accomplish its task.
 * 
 * Since the GXmlDataController also includes a network capabilities
 * different elements are managed by different GWE Servers. To differentiate
 * between elements that this server is responsible for and elements
 * that other servers are managing, the concept of primary and secondary
 * elements is introduced. All known elements are stored in the same
 * storage, but the GXmlDataController keeps track of which elements
 * are primary and which are secondary.
 * 
 * The difference in handling primary and secondary elements is that
 * influences radiated from secondary elements are not transported
 * to other GWE Servers while influences from primary elements are
 * transported to every affected GWE Server.
 * 
 * For the XML protocol, please look at the technical documentation.
 * 
 * @todo keep track of online and offline GWE Servers (presence)
 * @todo proper shutdown (persistent storage,...)
 * @todo move most of the QMaps into the database, as soon as available
 */
 
class GXmlDataController : public GDataController
{
  Q_OBJECT
  
  protected:
  
    /**
     * The serializer is used to actually convert all data between
     * C++ objects and XML documents
     */
    GCoreXmlSerializer* Serializer;
    
    /**
     * The storage that is used by the data controller, most likely
     * a database backend.
     */
    GStorage* Storage;
    
    /**
     * The GXmlDataController class is able to handle distributed
     * world management.
     * 
     * For the communication between all GWE Server instances, a
     * network class is used.
     */
    GXmlNetwork* Network;
    
    /**
     * The GWE Server that is one level higher in the network hierarchy.
     * If it is an empty string it means that THIS server is the highest in the
     * hierarchy.
     *
     * If a master server is given on initialization, then it is contacted
     * after a successfull network initialization.
     */
    QString MasterServer;
    
    /**
     * Contains a list of GWE Servers one level ower in the network hierarchy.
     * 
     * These need to be contacted if this server goes down for example.
     */
//     QStringList ChildServers;
    
    /**
     * When receiving element data through the network, the sender
     * server is stored together with the received element ID in this
     * mapping.
     * 
     * This mapping can then be used to contact the correct servers for
     * updates or for sending influences.
     * 
     * Remember that all elements in this map must be secondary elements.
     */
//     QMap<GCS::GElementID,QString> ElementServerMapping;
    
    /**
     * Holds a list of element IDs which are primary elements on this server.
     * 
     * @todo IMPORTANT for persistance: GXmlStorage should keep primary and secondary elements separate
     */
//     QValueList<GCS::GElementID> PrimaryElements;
    
    /**
     * Holds a list of element IDs which are secondary elements on this server.
     * 
     * @todo IMPORTANT for persistance: GXmlStorage should keep primary and secondary elements separate
     */
//     QValueList<GCS::GElementID> SecondaryElements;
    
    /**
     * Includes all open elements.
     */
    QMap<GCS::GElementID,GCS::GElement*> OpenElements;
    
    /**
     * Holds the values of last propagations of elements.
     * This is used to avoid resending frequently changing
     * elements too often.
     * This is used for "general syndication to all known servers".
     * @see syndicateElementData(), PendingSyndication
     */
    QMap<GCS::GElementID,QDateTime> LastSyndicationTime;
    
    /**
     * Holds all elements that will be syndicated, but
     * are delayed because of too frequent syndication requests.
     * This is used for "general syndication to all known servers".
     * @see syndicateElementData(), processAgentChanged(), postSyndication(), LastSyndicationTime
     */
    QValueList<GCS::GElementID> PendingSyndication;

  public:
  
    /**
     * Constructor.
     * @param master_server should hold the network id of a server which is
     *            part of the already existing network, empty if this
     *            server is the first of the whole infrastructure.
     */
    GXmlDataController(GStorage* storage, GXmlNetwork* network, const QString& MasterServer="", QObject *parent = 0, const char *name = 0);
    
    /**
     * Virtual Destructor.
     */
    virtual ~GXmlDataController();
    
    // GDataController implementation //
    
    virtual const GCS::GElement* read(const GCS::GElementID& ) const;
    
    virtual QValueList<GCS::GElementID> getChildren(const GCS::GElementID& parent) const;
    
    virtual GCS::GElement* open(const GCS::GElementID& );
    
    virtual GCS::GElement* getOpenElement(const GCS::GElementID& );

    virtual const QValueList<GCS::GElementID> getListOfOpenElements();

    virtual const QValueList<GCS::GElementID> getListOfAllElements();
    
  public slots:
  
    /**
     * This method adds a primary element. An added element
     * is open by default.
     * @see GDataController::add()
     */
    virtual bool add(GCS::GElement* );

    virtual bool writeOpenElementToStorage(const GCS::GElementID& );

    virtual bool close(const GCS::GElementID& );

    virtual bool postDelete(const GCS::GElementID& );

  public:

    /**
     * @return true if this GWE Server is the master server of a network.
     */
    bool isMasterServer();

    /**
     * This method uses the ElementServerMapping.
     * @return the server, if known, of the given Element ID, otherwise an empty string.
     */
    QString getManagingServerOfElement(const GCS::GElementID&) const;
    
    /**
     * @return all known servers, including the master server without duplicated entries.
     */
    QStringList getAllKnownServers() const;

  protected slots:

    /**
     * Called when the application exits,
     * properly unregisters from master server and
     * shuts down network connection.
     */
    virtual void shutdown();
    
    /**
     * Registers this GWE Server at the master GWE Server,
     * usually after a successfull network initialization.
     */
    virtual void registerWithMaster();

    /**
     * This actually sends an unregister message to the
     * master server, it is automatically called when
     * the application is about to quit.
     */
    virtual void unregisterFromMaster();
    
    /**
     * Sends an unregister message to all known servers,
     * including the master server.
     */
    virtual void unregisterFromAllKnownServers();

    /**
     * Basically just checks the amount of left IDs and eventually
     * calls requestFreeIDs()
     */
    virtual void checkFreeIDs();

    /**
     * Generates a requestfreeids message and sends
     * it to the master server.
     */
    virtual void requestFreeIDs(unsigned long amount);
    
    /**
     * Sends given amount of free GElementIDs to given server.
     */
    virtual void sendFreeIDs(QString server, unsigned long amount);
    
    /**
     * Adds given element to list of pending syndications.
     * @see PendingSyndication
     */
    virtual void postSyndication(const GCS::GElementID& id);
    
    /**
     * Basically checks if there are elements that
     * still need to be syndicated and syndicates them
     * if enough time has elapsed since last syndication.
     * @see PendingSyndication
     */
    virtual void checkElementsForSyndication();

    /**
     * Propagates given element to given server.
     */
    virtual void syndicateElementDataToServer(const GCS::GElementID& id,const QString& server);

    /**
     * Forces propagation of element data to the master server
     * and all child servers.
     *
     * Element data is not propagated to the owner (managing server)
     * of given element.
     */
    virtual void syndicateElementData(const GCS::GElementID& id);

    /**
     * Propagates all known element data to given server.
     * This is useful for servers that just get online for example.
     */
    virtual void syndicateAllElementDataToServer(const QString& server);
    
    /**
     * Updates server presence.
     * Usually connected to presenceChanged() signals from XML Network.
     */
    virtual void updateServerPresence(QString server, bool available);


    //BEGIN ELEMENT OBSERVATION
    
    /**
     * Called for every opened element, connects some signals and slots,
     * registers the element in the OpenElements map.
     */
    virtual void prepareOpenedElement(GCS::GElement* element);

    /**
     * When an element is reparented, other servers and elements need to be notified about it.
     * This slot generates the appropriate messages. It is connected to open elements by default,
     * also to elements that just got "added".
     *
     * @todo Currently only messages are generated for servers managing the old and new parent,
     *       but it might occur that this server holds a parent that detects a child moving out
     *       of itself (if the parent gets smaller). In such a case the reparented element as such
     *       is not located on this server although the reparenting was detected here and the
     *       managing server of the reparented element needs to be notified as well (receive data
     *       handles all three cases).
     */
    virtual void processReparenting(GCS::GElement* element, const GCS::GElementID& oldParent, const GCS::GElementID& newParent, const GCS::GMatrix44& transformation);
    
    /**
     * When an element is influenced and this server is not the managing
     * server of the affected element the influence is transported to the
     * managing server.
     * 
     * This slot is usually connected to the influenceReceived() signal from
     * elements.
     */
    virtual void processInfluencing(const GCS::GElementInfluence& influence);
    
    /**
     * Listens to agentChanged() signals of open elements and
     * requests a global syndication of this element.
     * @see PendingSyndication
     */
    virtual void processAgentChanged(const GCS::GAgent& agent);
    
    /**
     * Unregisters given server from this server and does all
     * required postprocessing such an event requires.
     */
    virtual void processUnregister(const QString& server);

  public slots:

    /**
     * Connected to the XML network for receiving data.
     * This implements the XML protocol used for GWE communication.
     */
    virtual void receiveData(QDomElement data, const QString& sender);

//    /**
//      * This data controller differentiates between primary and secondary
//      * elements in order to know which are managed on this server and
//      * which elements are the responsibility of other GWE Servers.
//      * 
//      * An added secondary element is open by default after it is added.
//      * 
//      * Use this slot to insert a secondary element. The add() slot
//      * inserts a primary element.
//      */
//     virtual void addSecondary(GCS::GElement*);

    /**
     * Checks complete storage consistency. The return value is
     * stored in the ok parameter.
     * @param ok the return value is stored here as well.
     * @return TRUE when storage is fully consistent, otherwise FALSE.
     */
    virtual bool checkConsistency(bool& ok);
    
    //BEGIN SLOTS / SIGNALS FOR USER INTERACTION
    
  public slots:
    
    /**
     * Sends a user message to given destination.
     * @see userMessageReceived()
     */
    void sendUserMessage(QString message, QString destination);
    
  signals:
    
    /**
     * Emitted when a user message (a "pure" XMPP message tag with text
     * content) is received, this can be used in the user interface of a
     * client for example.
     * @param internal is true when the message comes from inside the G Universe
     */
    void userMessageReceived(QString message, QString sender, bool internal);
    
    /**
     * Emitted when a server presence state changes.
     * This signal is used even when a
     * server changes from external to internal state, which happens
     * when registering - it first gets available as an external server,
     * then registers and becomes an internal server.
     * @param internal is true when the server is inside the G Universe
     */
    void serverPresenceChanged(QString server, bool available, bool internal);

    //END SLOTS / SIGNALS FOR USER INTERACTION
};

}

#endif //GXMLDATACONTROLLER_H
