/*****************************************************************************/
/*!
 * \file context.h
 * 
 * Author: Clark Barrett
 * 
 * Created: Tue Dec 31 19:07:38 2002
 *
 * <hr>
 * Copyright (C) 2003 by the Board of Trustees of Leland Stanford
 * Junior University and by New York University. 
 *
 * License to use, copy, modify, sell and/or distribute this software
 * and its documentation for any purpose is hereby granted without
 * royalty, subject to the terms and conditions defined in the \ref
 * LICENSE file provided with this distribution.  In particular:
 *
 * - The above copyright notice and this permission notice must appear
 * in all copies of the software and related documentation.
 *
 * - THE SOFTWARE IS PROVIDED "AS-IS", WITHOUT ANY WARRANTIES,
 * EXPRESSED OR IMPLIED.  USE IT AT YOUR OWN RISK.
 * 
 * <hr>
 * 
 */
/*****************************************************************************/


#ifndef _cvcl__include__context_h_
#define _cvcl__include__context_h_

#include <string>
#include <vector>
#include "debug.h"


namespace CVCL {

/****************************************************************************/
/*! \defgroup Context Context Management
 *  \ingroup BuildingBlocks
 * Infrastructure for backtrackable data structures.
 * @{
 */
/****************************************************************************/

class Context;
class ContextManager;
class ContextNotifyObj;
class ContextObj;
class ContextObjChain;

/****************************************************************************/
/*! 
 * Author: Clark Barrett
 *
 * Created: Thu Feb 13 00:19:15 2003
 *
 * A scope encapsulates the portion of a context which has changed
 * since the last call to push().  Thus, when pop() is called,
 * everything in this scope is restored to its previous state.
 */
 /****************************************************************************/

class Scope {
  friend class ContextObj;
  friend class ContextObjChain;
  friend class CDFlags;
  //! Context that created this scope
  Context* d_context;

  //! Previous scope in this context
  Scope* d_prevScope;

  //! Scope level
  int d_level;

  /*! @brief Linked list of objects which are "current" in this scope,
    and thus need to be restored when the scope is deleted */
  ContextObjChain* d_restoreChain;

  //! Deleted ContextObj's (queued up for deletion during restore())
  std::vector<ContextObjChain*> d_deleted;

  //! Set while inside restore() or ~Scope() methods
  bool d_delayDelete;

  //! Called by ContextObj when created
  void addToChain(ContextObjChain* obj);

public:
  //! Constructor
  Scope(Context* context, Scope* prevScope = NULL);
  //! Delete the scope (only free up memory, do not call restore())
  ~Scope();

  //! Access functions
  Scope* prevScope() const { return d_prevScope; }
  int level(void) const { return d_level; }
  bool isCurrent(void) const;
  Scope* topScope() const;

  //! Restore all the values
  void restore(void);
};

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Class: ContextObjChain						     //
// Author: Sergey Berezin                                                    //
// Created: Wed Mar 12 11:25:22 2003					     //

/*! Description: An element of a doubly linked list holding a copy of
 * ContextObj in a scope.  It is made separate from ContextObj to keep
 * the list pointers valid in all scopes at all times, so that the
 * object can be easily removed from the list when the master
 * ContextObj is destroyed. */
///////////////////////////////////////////////////////////////////////////////
class ContextObjChain {
friend class Scope;
friend class ContextObj;
friend class CDFlags;
private:
  //! Next link in chain
  ContextObjChain* d_restoreChainNext;

  /*! @brief Pointer to the pointer of the previous object which
  points to us.  This makes a doubly-linked list for easy element
  deletion */
  ContextObjChain** d_restoreChainPrev;

  //! Pointer to the previous copy which belongs to the same master
  ContextObjChain* d_restore;

  //! Pointer to copy of master to be restored when restore() is called
  ContextObj* d_data;

  //! Pointer to the master object
  ContextObj* d_master;

  //! Private constructor (only friends can use it)
  ContextObjChain(ContextObj* data, ContextObj* master, 
		  ContextObjChain* restore);

  //! Restore from d_data to d_master
  ContextObjChain* restore(void);
public:
  //! Destructor
  ~ContextObjChain();
  IF_DEBUG(std::string name() const;);
};

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Class: ContextObj							     //
// Author: Clark Barrett                                                     //
// Created: Thu Feb 13 00:21:13 2003					     //

/*!  Description: This is a generic class from which all objects that
 * are context-dependent should inherit.  Subclasses need to implement
 * makeCopy, restoreData, and setNull.
 */
///////////////////////////////////////////////////////////////////////////////
class ContextObj {
friend class Scope;
friend class ContextObjChain;
friend class CDFlags;
private:
  //! Last scope in which this object was modified.
  Scope* d_scope;

  /*! @brief The list of values on previous scopes; our destructor
   *  should clean up those. */
  ContextObjChain* d_restore;

  IF_DEBUG(std::string d_name);
  IF_DEBUG(bool d_active);

  //! Update on the given scope, on the current scope if 'scope' == 0
  void update(int scope = -1);

protected:
  //! Copy constructor (defined mainly for debugging purposes)
  ContextObj(const ContextObj& co);
  //! Assignment operator (defined mainly for debugging purposes)
  ContextObj& operator=(const ContextObj& co);
  /*! @brief Make a copy of the current object so it can be restored
   * to its current state */
  virtual ContextObj* makeCopy(void) = 0;

  //! Restore the current object from the given data
  virtual void restoreData(ContextObj* data) {
    FatalAssert(false, 
                "ContextObj::restoreData(): call in the base abstract class");
  }

  //! Set the current object to be invalid
  virtual void setNull(void) = 0;

  //! Return our name (for debugging)
  IF_DEBUG(virtual std::string name() const { return d_name; });

public:
  //! Create a new ContextObj.
  /*!
   * The initial scope is set to the bottom scope by default, to
   * reduce the work of pop() (otherwise, if the object is defined
   * only on a very high scope, its scope will be moved down with each
   * pop).  If 'atBottomScope' == false, the scope is set to the
   * current scope. 
   */
  ContextObj(Context* context, bool atBottomScope = true);
  virtual ~ContextObj();

  int level() const { return (d_scope==NULL)? 0 : d_scope->level(); }
  bool isCurrent(int scope = -1) const {
    if(scope >= 0) return d_scope->level() == scope;
    else return d_scope->isCurrent();
  }
  void makeCurrent(int scope = -1) { if (!isCurrent(scope)) update(scope); }
  IF_DEBUG(void setName(const std::string& name) { d_name=name; });
};

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// Class: Context							     //
// Author: Clark Barrett                                                     //
// Created: Thu Feb 13 00:24:59 2003					     //
/*!
 * Encapsulates the general notion of stack-based saving and restoring
 * of a database.
 */
///////////////////////////////////////////////////////////////////////////////
class Context {
  //! Context Manager
  ContextManager* d_cm;

  //! Name of context
  std::string d_name;

  //! Context ID
  int d_id;

  //! Pointer to top and bottom scopes of context
  Scope* d_topScope;
  Scope* d_bottomScope;

  //! List of objects to notify with every pop
  std::vector<ContextNotifyObj*> d_notifyObjList;

public:
  Context(ContextManager* cm, const std::string& name, int id);
  ~Context();

  //! Access methods
  ContextManager* getCM() const { return d_cm; }
  const std::string& name() const { return d_name; }
  int id() const { return d_id; }
  Scope* topScope() const { return d_topScope; }
  Scope* bottomScope() const { return d_bottomScope; }
  int level() const { return d_topScope->level(); }

  // TODO: use custom memory manager
  void push() {
    d_topScope = new Scope(this, d_topScope);
    TRACE("context", "*** [context] Pushing scope to level ", level(), " {");
    IF_DEBUG(DebugCounter maxLevel(debugger.counter("max scope level")));
    IF_DEBUG(if(maxLevel<level()) maxLevel=level());
  }
  void pop();
  void popto(int toLevel);
  void addNotifyObj(ContextNotifyObj* obj) { d_notifyObjList.push_back(obj); }
  void deleteNotifyObj(ContextNotifyObj* obj);
};

// Have to define after Context class
inline bool Scope::isCurrent(void) const
  { return this == d_context->topScope(); }

inline Scope* Scope::topScope() const { return d_context->topScope(); }

/****************************************************************************/
//! Manager for multiple contexts.  Also holds current context.
/*!
 * Author: Clark Barrett
 *
 * Created: Thu Feb 13 00:26:29 2003
 */
/****************************************************************************/

class ContextManager {
  Context* d_curContext;
  std::vector<Context*> d_contexts;

public:
  ContextManager();
  ~ContextManager();

  void push() { d_curContext->push(); }
  void pop() { d_curContext->pop(); }
  void popto(int toLevel) { d_curContext->popto(toLevel); }
  int scopeLevel() { return d_curContext->level(); }
  Context* createContext(const std::string& name="");
  Context* getCurrentContext() { return d_curContext; }
  Context* switchContext(Context* context);
};

/****************************************************************************/
/*! Author: Clark Barrett                                                     
 *
 * Created: Sat Feb 22 16:21:47 2003					     
 *
 * Lightweight version of ContextObj: objects are simply notified
 * every time there's a pop. notifyPre() is called right before the
 * context is restored, and notify() is called after the context is
 * restored.
 */
/****************************************************************************/
 
class ContextNotifyObj {
  friend class Context;
protected:
  Context* d_context;
public:
  ContextNotifyObj(Context* context): d_context(context)
    { context->addNotifyObj(this); }
  virtual ~ContextNotifyObj() {
    // If we are being deleted before the context, remove ourselves
    // from the notify list.  However, if the context is deleted
    // before we are, then our d_context will be cleared from ~Context()
    if(d_context!=NULL) d_context->deleteNotifyObj(this);
  }
  virtual void notifyPre(void) {}
  virtual void notify(void) {}
};

/*@}*/  // end of group Context

}

#endif
