//-*-C++-*-
/*
**************************************************************************
                                 description
                             --------------------
    copyright            : (C) 2000-2001 by Andreas Zehender
    email                : zehender@kde.org
**************************************************************************

**************************************************************************
*                                                                        *
*  This program is free software; you can redistribute it and/or modify  *
*  it under the terms of the GNU General Public License as published by  *
*  the Free Software Foundation; either version 2 of the License, or     *
*  (at your option) any later version.                                   *
*                                                                        *
**************************************************************************/


#ifndef PMOBJECTMEMENTO_H
#define PMOBJECTMEMENTO_H

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "pmobject.h"
#include "pmvector.h"
#include "pmcolor.h"
#include "pmcommand.h"
#include <qstring.h>
#include <qptrlist.h>

/**
 * Class used by @ref PMObjectMemento to store one value.
 *
 * Similar to the variant type.
 *
 * Each piece of data has two IDs: The type of the povray class that stored
 * the data and a unique ID within this class.
 *
 * @see PMMementoDataIterator
 */

class PMMementoData
{
   /** Type of stored data */
   enum PMMementoDataType { PMMInt, PMMUnsigned, PMMDouble, PMMBool,
                            PMMThreeState, PMMString, PMMVector, PMMColor,
                            PMMObjectPointer };
public:
   /** Stores an integer */
   PMMementoData( PMObjectType objType, int vID, const int data );
   /** Stores an unsigned integer */
   PMMementoData( PMObjectType objType, int vID, const unsigned int data );
   /** Stores double */
   PMMementoData( PMObjectType objType, int vID, const double data );
   /** Stores a boolean */
   PMMementoData( PMObjectType objType, int vID, const bool data );
   /** Stores a @ref PMThreeState */
   PMMementoData( PMObjectType objType, int vID, const PMThreeState data );
   /** Stores a string */
   PMMementoData( PMObjectType objType, int vID, const QString& data );
   /** Stores a @ref PMVector */
   PMMementoData( PMObjectType objType, int vID, const PMVector& data );
   /** Stores a @ref PMColor */
   PMMementoData( PMObjectType objType, int vID, const PMColor& data );
   /** Stores a pointer to a PMObject */
   PMMementoData( PMObjectType objType, int vID, PMObject* obj );
   /** Deletes the data */
   ~PMMementoData( );

   /** Sets the integer value. Data type has to be PMMInt! */
   void setInt( const int data );
   /** Sets the unsigned value. Data type has to be PMMUnsigned! */
   void setUnsigned( const unsigned int data );
   /** Sets the double value. Data type has to be PMMDouble! */
   void setDouble( const double data );
   /** Sets the boolean value. Data type has to be PMMBool! */
   void setBool( const bool data );
   /** Sets the PMThreeState value. Data type has to be PMMThreeState! */
   void setThreeState( const PMThreeState data );
   /** Sets the string data. Data type has to be PMMString! */
   void setString( const QString& data );
   /** Sets the vector data. Data type has to be PMMVector! */
   void setVector( const PMVector& data );
   /** Sets the color data. Data type has to be PMMColor! */
   void setColor( const PMColor& data );
   /** Sets the object pointer. Data type has to be PMMObjectPointer! */ 
   void setObject( PMObject* o );
   
   /** Returns the integer value. Data type has to be PMMInt! */
   int intData( ) const;
   /** Returns the double value. Data type has to be PMMDouble! */
   double doubleData( ) const;
   /** Returns the boolean value. Data type has to be PMMBool! */
   bool boolData( ) const;
   /** Returns the PMThreeState value. Data type has to be PMMThreeState! */
   PMThreeState threeStateData( ) const;
   /** Returns the string data. Data type has to be PMMString! */
   QString stringData( ) const;
   /** Returns the vector data. Data type has to be PMMVector! */
   PMVector vectorData( ) const;
   /** Returns the color data. Data type has to be PMMColor! */
   PMColor colorData( ) const;
   /** Returns the object pointer. Data type has to be PMMObjectPointer! */
   PMObject* objectData( ) const;

   /** Returns the type of the stored data */
   PMMementoDataType dataType( ) const { return m_dataType; }
   /** Returns the object type of the povray class that stored the data */
   PMObjectType objectType( ) const { return m_objectType; }
   /** Returns the ID of the stored value */
   int valueID( ) const { return m_valueID; }
private:
   /** a pointer to the stored data */
   void* m_pData;
   /** Type of the data */
   PMMementoDataType m_dataType;
   /** Class that stored the information */
   PMObjectType m_objectType;
   /** The unique ID within the m_objectType */
   int m_valueID;
};

typedef QPtrList<PMMementoData> PMMementoDataList;


/**
 * Helper class to store information about changed objects in @ref PMMemento
 */
class PMObjectChange
{
public:
   /**
    * Creates change information for the object obj.
    *
    * mode is a bitwise combination of @ref PMChange constants.
    */
   PMObjectChange( PMObject* obj, int mode )
   {
      m_pObject = obj;
      m_mode = mode;
   }
   PMObject* object( ) const { return m_pObject; }
   int mode( ) const { return m_mode; }
   void addMode( int mode ) { m_mode |= mode; }
private:
   PMObject* m_pObject;
   int m_mode;
};

typedef QPtrList<PMObjectChange> PMObjectChangeList;
typedef QPtrListIterator<PMObjectChange> PMObjectChangeListIterator;

/**
 * Class that stores the data of povray objects for undo/redo information
 *
 * This class should provide enough functionality for most objects. If not,
 * subclass this class.
 *
 * All objects have to use the memento class of its base class or an inherited
 * one.
 *
 * Only the changed attributes of an object are saved.
 *
 * See class @ref PMMementoData to see how values are stored.
 */
class PMMemento
{
   friend class PMMementoDataIterator;
public:
   /** Creates a memento for the object originator */
   PMMemento( PMObject* originator );
   /** Deletes the memento */
   virtual ~PMMemento( );

   /** Returns a pointer to the originator */
   PMObject* originator( ) const { return m_pOriginator; }
   /** Returns a pointer to the memento data or 0 if this value is not
    * stored */
   PMMementoData* findData( PMObjectType objType, int vID ) const;
   /** Adds the data object to the list of stored data. The menento may not
    * contain this information (objType and vID).
    *
    * The memento becomes the owner of this object.*/
   void addData( PMMementoData* data );

   /** Adds an integer data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const int data );

   /** Adds an unsigned data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const unsigned int data );

   /** Adds an double data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const double data );

   /** Adds an bool data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const bool data );

   /** Adds an @ref PMThreeState data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const PMThreeState data );

   /** Adds an @ref QString data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const QString& data );

   /** Adds an @ref PMVector data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const PMVector& data );

   /** Adds an @ref PMColor data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, const PMColor& data );

   /** Adds an PMObject pointer data object. Will be ignored if the memento
    * already contains this data */
   void addData( PMObjectType objType, int vID, PMObject* obj );
   
   /** Call this to store an id change */
   void addIDChange( PMObjectType objType, int vID, const QString& data );

   /** Returns true if the memento contains changed data */
   bool containsChanges( ) const { return ( m_changedObjects.count( ) > 0 ); }
   
   /** Call this function, if you stored some data that changed
    * the view structure of the originator */
   void setViewStructureChanged( ) { addChange( PMCViewStructure ); }
   /** Call this function, if you stored some data that changed
    * the name or pixmap of the originator */
   void setDescriptionChanged( ) { addChange( PMCDescription ); }
   
   /** Returns true if the id of a @ref PMDeclare was changed */
   bool idChanged( ) const { return m_pIDData != 0; }
   /** Returns the old id */
   QString oldID( ) const;
   /**
    * If one object is changed, other objects can be changed as well.
    *
    * For example, if the linked declare of an object link is changed,
    * the old and new declare are changed as well (Remove the old link,
    * add the new link).
    *
    * This function returns an iterator to the list of all changed objects.
    * The first object is the originator.
    */
   PMObjectChangeListIterator changedObjects( )
   { return PMObjectChangeListIterator( m_changedObjects ); }
   /** Adds the object to the list of changed objects.
    * Note that the originator is added automatically */
   void addChangedObject( PMObject* obj, int mode );
protected:
   /** Adds the change to the originator change object */
   void addChange( int mode );
private:   
   /** The stored values */
   PMMementoDataList m_data;
   /** Additional pointer to the memento data for id changes of
    * @ref PMDeclare objects */
   PMMementoData* m_pIDData;
   /** List of changes */
   QPtrList<PMObjectChange> m_changedObjects;
   PMObjectChange* m_pOriginatorChange;
   PMObject* m_pOriginator;
};


/** Iterator for memento data */
class PMMementoDataIterator : public QPtrListIterator<PMMementoData>
{
public:
   PMMementoDataIterator( const PMMemento& m )
         : QPtrListIterator<PMMementoData>( m.m_data )
   {
   }
   PMMementoDataIterator( const PMMemento* m )
         : QPtrListIterator<PMMementoData>( m->m_data )
   {
   }
   PMMementoDataIterator( const PMMementoDataList& l )
         : QPtrListIterator<PMMementoData>( l )
   {
   }
};

#endif
