#ifndef H_CORAL_TRANSACTIONMANAGER
#define H_CORAL_TRANSACTIONMANAGER
// This file is part of the Coral project.
// Please read the copyright notice in the LICENSE file.

#include <memory>

#include <coral/coral.h>
#include <coral/lib/autovector.h>
#include <coral/lib/List.h>
#include <coral/lib/noncopy.h>
#include <coral/core/Element.h>
#include <coral/core/Project.h>

#include <iostream>
#include <coral/core/python_compat.h>

NAMESPACE_BEGIN(coral);
NAMESPACE_BEGIN(Transaction);

class EXPORTFWD_coral Manager;
class EXPORTFWD_coral Transaction;
class EXPORTFWD_coral Command;
#ifndef SWIG
class PauseTransaction;
extern CORAL_HIDDEN Manager* theTM;

#endif

/*
 * These are attached to the Manager.
 * One per transaction.
 */
class EXPORT_coral Transaction
: public ref_counted, protected List {
private:
     friend class Manager;
     /* These are the individual commands. */
     typedef autounrefvector<Command*> commands_type;
     commands_type m_commands;

public:
     Transaction();
     ~Transaction() throw ();
     std::string __str__() const;
     size_t size() const throw ();
#ifndef SWIG
     Command* operator[](int pos);
#endif
     Command* __getitem__(int pos) {
	  return (*this)[pos];
     }

};

#ifndef SWIG
/**
 * Forgets the previous transaction command upon destruction.
 * Can be overridden by calling release().
 */
class EXPORT_coral ForgetPreviousCommand {
	bool m_destroy;
public:
	ForgetPreviousCommand() :
		m_destroy(true) {
		;
	}
	~ForgetPreviousCommand() throw ();
	void release() { m_destroy = false; }
};

#endif

/*
 * The individual commands in a
 * Transaction
 *
 */
class EXPORT_coral Command : public ref_counted, public noncopy {
protected:
     Command() { };
public:
     virtual ~Command() throw ();
     virtual std::string __str__() const CORAL_VIRTUAL;
     virtual void execute() throw ();
     virtual void unexecute() throw () CORAL_VIRTUAL;
};

class EXPORT_coral ElementCommand : public Command {
protected:
     Atom* e;
     ElementCommand(Atom* elem ) {
	  coral_assert(elem); this->e = elem; elem->ref();
     }
public:
     virtual ~ElementCommand() throw () { this->e->unref(); }
     coral::Atom* getAtom() const throw () {
	  return this->e;
     }
};

class EXPORT_coral NewProjectCommand : public Command {
protected:
	Project* m_newstorage;
public:
	NewProjectCommand(Project* s) : m_newstorage(s) {
		coral_assert(s); s->ref();
	}
	~NewProjectCommand() throw () { m_newstorage->unref(); } 
	Project* getNewProject() const throw () { return m_newstorage; }
#ifndef SWIG
	std::string __str__() const;
	void unexecute() throw () { ; }
#endif
};

class EXPORT_coral ProjectNameChangeCommand : public Command {
protected:
	Project* m_storage;
	std::string m_old_filename;
	std::string m_old_href_name;
public:
	ProjectNameChangeCommand(Project* s,
				 const std::string& old_filename,
				 const std::string& old_href_name);
	~ProjectNameChangeCommand() throw () { m_storage->unref(); } 
	Project* getProject() const throw () { return m_storage; }
	const std::string& getOldFilename() const throw () { return m_old_filename; }
	const std::string& getOldHrefFilename() const throw () { return m_old_href_name; }
#ifndef SWIG
	std::string __str__() const;
	void unexecute() throw ();
#endif
};


class EXPORT_coral ProjectChangeCommand : public ElementCommand {
protected:
     Project* m_old;
public:
     ProjectChangeCommand(Element* e, Project* old);
     ~ProjectChangeCommand() throw ();
     Project* getOldProject() const throw () { return m_old; }
#ifndef SWIG
     std::string __str__() const;
     void unexecute() throw ();
#endif
};

class EXPORT_coral ChangeElementPrimitiveCommand : public ElementCommand {
private:
     ElementPrimitive* new_atom;
public:
     ChangeElementPrimitiveCommand(coral::ElementPrimitive* elem)
	  : ElementCommand(elem->clone()) {
	  new_atom = elem;
	  new_atom->ref();
     }
	  virtual ~ChangeElementPrimitiveCommand() throw () {
	       this->new_atom->unref();
	  }
	  coral::ElementPrimitive* getNewAtom() const throw () {
	       return this->new_atom;
	  }
#ifndef SWIG
	  std::string __str__() const;
	  void unexecute() throw ();
#endif
};

class EXPORT_coral ElementChangeSlotCommand : public ElementCommand {
private:
     std::auto_ptr<coral::Slot> previous;
public:
     ElementChangeSlotCommand(Element* elem, Slot* theprevious)
	  : ElementCommand(elem),
	  previous(theprevious->clone()) { ; }
	  virtual ~ElementChangeSlotCommand() throw () { ; }
	  coral::Slot* getSlot() const throw () {
	       return this->previous.get();
	  }
#ifndef SWIG
	  std::string __str__() const;
	  void unexecute() throw ();
#endif
};

class EXPORT_coral ElementNewCommand : public ElementCommand {
public:
     ElementNewCommand(coral::Element* elem)
	  : ElementCommand(elem) { ; }
	  virtual ~ElementNewCommand() throw () { ; }
	  coral::Element* getNewElement() const throw () {
	       return static_cast<coral::Element*>(this->e);
	  }
#ifndef SWIG
	  std::string __str__() const;
	  void unexecute() throw ();
#endif
};

class EXPORT_coral ElementUUIDCommand : public ElementCommand {
private:
     std::string XMIid;
public:
     ElementUUIDCommand(coral::Element* elem,
		 const std::string& xmiid) 
	  : ElementCommand(elem), XMIid(xmiid) { ; }
	  virtual ~ElementUUIDCommand() throw () { ; }
	  std::string getOldXMIid() const throw () {
	       return this->XMIid;
	  }
#ifndef SWIG
	  std::string __str__() const;
	  void unexecute() throw ();
#endif
};

/*
 * The Transaction Manager.
 */
extern EXPORT_coral Manager* getTransactionManager();

class EXPORT_coral Manager {
private:
	enum transaction_type { TRANSACTION_REDO,
				TRANSACTION_UNDO,
				TRANSACTION_ORDINARY };
     typedef std::list<PyObject*> observers_type;
     observers_type  m_observers;
     typedef std::list<PyObject*> busyObservers_type;
     busyObservers_type  m_busyObservers;
     bool m_active;
     bool m_strict;
     int m_n_transactions;
     /* All items in this manager have this as
      * their base. */
     List m_transactions;
     /* beginModifyCmd recursion depth. */
     int m_inModifyCmd;
     /* We have executed this command, we're going
      * to move to the next.
      * So at start, currentCmd == transactions
      */
     Transaction* m_currentCmd;
     transaction_type m_current_tr_in_progress;
#ifndef SWIG			
     friend Manager* getTransactionManager();
     friend class PauseTransaction;
#endif
     CORAL_HIDDEN Manager();
     /**
      * Destroys elements that should be deleted
      */
     CORAL_HIDDEN void orphanManager();
     /**
      * Calls python observers.
      */
     CORAL_HIDDEN void callObservers(transaction_type forwards);
     CORAL_HIDDEN void callBusyObservers(bool isBusy);
     int m_amount_of_items_in_current_trans;
     CORAL_HIDDEN void trimPast_w_history(int limit) throw ();
     int m_history_limit;

     /* Remember which Element-Property pairs have been
      * altered, and don't issue extra Commands to the Transaction.
      * This saves space and processing time. */
     typedef std::pair<const coral::Element*, const coral::Property*> content_t;
     typedef std::set<content_t> watch_t;
     watch_t m_watch;
public:
     CORAL_HIDDEN ~Manager() throw ();
     CORAL_HIDDEN bool already_modified(const coral::Element*,
					const coral::Property*) const;
#ifndef SWIG
     /**
      * Is the Transaction Manager active at the moment and without pauses?
      * This is mostly used internally by the core.
      */
     CORAL_HIDDEN bool internalIsActive() throw ();
#endif
     /**
      * Are we currently in an undo or a redo phase, instead of
      * an ordinary transaction?
      */
     bool isUndoOrRedo() throw ();
     /**
      * Observers are called at the end of each
      * transaction, in the order they were added.
      */
     void addObserver(PyObject*);
     void addBusyObserver(PyObject*);
     /**
      * Remove an observer.
      */
     void removeBusyObserver(PyObject*);
     void removeObserver(PyObject*);

     /**
      * Amount of Transactions in history
      */
     int getAmountOfTransactions() const throw ();
     /*
      * What is our current modification?
      * 0 = first
      * ...
      * ...
      * amountOfModications-1 = last
      * amountOfModifications = invalid
      */
     int getCurrentTransactionPosition() const throw ();
     Transaction* getTransaction(int pos) throw ();
     /**
      * Returns the current transaction, whether we're
      * in an ordinary, undo, or redo phase.
      */
     Transaction* getCurrentTransaction() const throw ();

     /**
      * Starts a new transaction, or continues an old one.
      */
     void beginModifyCmd(bool strict = false) throw ();
#ifndef SWIG
     void partialModifyCmd(Command* c) throw ();
     CORAL_HIDDEN void __forget_previous_Cmd() throw ();
#endif
     /**
      * Ends a transaction. If it's the last one matching
      * a beginModifyCmd(), the observers are run.
      */
     bool endModifyCmd() throw (std::exception);
     /* same as beginModifyCmd */
     void beginTransaction(bool strict = false) throw ();
     bool endTransaction() throw ();
     void beginStrictTransaction() throw () {
	  this->beginTransaction(true);
     }
     bool endStrictTransaction() throw () {
	  return this->endTransaction();
     }
     /*
      * Returns true if we're in the middle of a transaction
      */
     bool isCreatingTransaction() throw ();

     /**
      * Returns the history limit, i.e. the amount of
      * transactions that we remember and thus can undo/redo.
      */
     bool getHistoryLimit() throw () {
	  return m_history_limit;
     }
     /**
      * Sets the history limit.
      */
     void setHistoryLimit(int limit ) throw () {
	  m_history_limit = limit;
     }
     void trimPast() throw () {
	  trimPast_w_history(0);
     }
     void trimFuture() throw ();
     void clear() throw ();
     /**
      * Returns true if we can undo now
      */
     bool canUndo() throw ();
     /**
      * Returns true if we can redo now
      */
     bool canRedo() throw ();
     /**
      * Perform an undo. The individual commands in the current
      * transaction are run, and the observers are run.
      * NOTE! The observers may not change the model in _any_ way.
      */
     void undo() throw ();
     /**
      * Perform an redo. The individual commands in the current
      * transaction are run, and the observers are run.
      * NOTE! The observers may not change the model in _any_ way.
      */
     void redo() throw ();
};

#ifndef SWIG

class MakeTransaction : public noncopy {
public:
     MakeTransaction();
     ~MakeTransaction();
};

#endif

#ifdef COMPILING_coral
#ifndef SWIG
/*
 * Use to conditionally pause transactions. See Model.cpp.
 * Only used internally.
 * Only usable stack allocated, not heap allocated.
 */
class PauseTransaction : public noncopy {
private:
     bool m_pause;
public:
     PauseTransaction(bool yes = true);
     ~PauseTransaction() throw ();
     void setPause(bool yes) throw ();
};
#endif
#endif
NAMESPACE_END();
NAMESPACE_END();


#endif
