/*****************************************************************************/
/*!
 * \file context.cpp
 * 
 * Author: Clark Barrett
 * 
 * Created: Fri Jan 17 14:30:37 2003
 *
 * <hr>
 *
 * 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.
 * 
 * <hr>
 * 
 */
/*****************************************************************************/


#include "context.h"


using namespace CVC3;
using namespace std;


vector<char*> ContextMemoryManager::s_freePages;


///////////////////////////////////////////////////////////////////////////////
// Scope methods                                                             //
///////////////////////////////////////////////////////////////////////////////


void Scope::finalize(void)
{
  ContextObjChain* obj = d_restoreChain;
  while (obj != NULL) {
    ContextObjChain* tmp = obj->d_restoreChainNext;
    // When called from ~ContextManager(), the master objects may
    // still point to this scope.  Disconnect them here.
    if (obj->d_master != NULL) {
      if (obj->d_master->d_scope == this)
        obj->d_master->d_scope = NULL;
      if (obj->d_master->d_restore == obj)
        obj->d_master->d_restore = NULL;
    }
    obj = tmp;
  }
}


void Scope::check(void)
{
  IF_DEBUG(
  ContextObjChain* obj = d_restoreChain;
  if (debugger.trace("memory") && obj != NULL) {
    ostream& os = debugger.getOS();
    int n(0);
    ContextObjChain* tmp = obj;
    while(tmp != NULL) {
      tmp = tmp->d_restoreChainNext;
      n++;
    }
    os << "*** Warning: ~Scope(): found "<< n << " leaked objects "
       << "in scope " << d_level << ":" << endl;
    if (debugger.flag("memory leaks")) {
      tmp = obj;
      while(tmp != NULL) {
        os << tmp->name() << "\n";
        tmp = tmp->d_restoreChainNext;
      }
    }
    os << flush;
  }
  )
}


///////////////////////////////////////////////////////////////////////////////
// ContextObjChain methods                                                   //
///////////////////////////////////////////////////////////////////////////////


ContextObjChain* ContextObjChain::restore(void)
{
  // Assign 'next' pointer only when the master object is restored,
  // since this may change our next pointer (master may have other
  // context objects removed).
  DebugAssert(d_master != NULL, "How did this happen");
  //  if (d_master == NULL) return d_restoreChainNext;
  ContextObjChain* next;
  DebugAssert(d_data != NULL, "Shouldn't happen");
//   if (d_data == NULL) {
//     d_master->setNull();
//     d_master->d_scope = d_master->d_scope->prevScope();
//     DebugAssert(d_restore==NULL,"Expected NULL");
//     next = d_restoreChainNext;
//     d_master->d_scope->addToChain(this);
//   }
//   else {
    d_master->restoreData(d_data);
    d_master->d_scope = d_data->d_scope;
    d_master->d_restore = d_restore;
    next = d_restoreChainNext;
    if (d_data != NULL) delete d_data;
    DebugAssert(d_master->d_restore != this, "points to self");
//   }
  return next;
}


#ifdef DEBUG
std::string ContextObjChain::name() const
{
  DebugAssert(d_master != NULL, "NULL master");
  return d_master->name();
}
#endif


///////////////////////////////////////////////////////////////////////////////
// ContextObj methods                                                        //
///////////////////////////////////////////////////////////////////////////////


void ContextObj::update(int scope)
{
  Scope* tmpScope = d_scope;
  DebugAssert(scope < 0 || d_scope->level() <= scope,
	      "ContextObj::update(scope="+int2string(scope)
	      +"): scope < d_scope->level() = "
	      +int2string(d_scope->level()));
  d_scope = d_scope->topScope();
  if (scope >= 0) {
    // Fetch the specified scope
    for(int i=level(); i>scope; --i) {
      d_scope = d_scope->prevScope();
      DebugAssert(d_scope != NULL, "ContextObj::update["
		  +name()+"]: d_scope == NULL");
    }
  }
  ContextObj* data = makeCopy(getCMM());
  data->d_scope = tmpScope;
  // The destructor of the copy should not destroy our older copies
  data->d_restore=NULL;
  IF_DEBUG(data->setName(name()+" [copy]"));
  d_restore = new(getCMM()) ContextObjChain(data, this, d_restore);
  d_scope->addToChain(d_restore);
}


ContextObj::~ContextObj()
{
  // Delete our restore copies
  IF_DEBUG(FatalAssert(d_active, "~ContextObj["+name()+"]"));
  IF_DEBUG(d_active=false); 
  for(ContextObjChain* obj = d_restore; obj != NULL; ) {
    ContextObjChain* tmp = obj->d_restore;
    // Remove the object from the restore chain
    if(obj->d_restoreChainNext != NULL)
      obj->d_restoreChainNext->d_restoreChainPrev = obj->d_restoreChainPrev;
    *(obj->d_restoreChainPrev) = obj->d_restoreChainNext;
    //    if (obj->d_data != NULL) delete obj->d_data;
    obj->d_master = NULL;
    if (tmp == NULL) {
      delete obj;
      free(obj);
      break;
    }
    obj = tmp;
  }
  //  TRACE("context verbose", "~ContextObj()[", this, "] }");
}


///////////////////////////////////////////////////////////////////////////////
// Context methods                                                           //
///////////////////////////////////////////////////////////////////////////////


Context::Context(ContextManager* cm, const string& name, int id)
  : d_cm(cm), d_name(name), d_id(id)
{
  ContextMemoryManager* cmm = new ContextMemoryManager();
  d_topScope = new(cmm) Scope(this, cmm);
  d_bottomScope = d_topScope;
  TRACE("context", "*** [context] Creating new context: name = "
	+ name + "id = ", id, "");
}


// Don't pop, just delete everything.  At this point, most of the
// system is already destroyed, popping may be dangerous.
Context::~Context()
{
  // popto(0);
  Scope* top = d_topScope;
  while(top != NULL) {
    top = d_topScope->prevScope();
    d_topScope->finalize();
    delete d_topScope->getCMM();
    d_topScope = top;
  }
  while (!d_cmmStack.empty()) {
    delete d_cmmStack.back();
    d_cmmStack.pop_back();
  }
  ContextMemoryManager::garbageCollect();
  // Erase ourselves from the notify objects, so they don't call us
  for(vector<ContextNotifyObj*>::iterator i=d_notifyObjList.begin(),
	iend=d_notifyObjList.end(); i!=iend; ++i) {
    (*i)->d_context = NULL;
  }
}


void Context::push()
{
  IF_DEBUG(
    string indentStr(level(), ' ');
    TRACE("pushpop", indentStr, "Push", " {");
  )
  ContextMemoryManager* cmm;
  if (!d_cmmStack.empty()) {
    cmm = d_cmmStack.back();
    d_cmmStack.pop_back();
  }
  else {
    cmm = new ContextMemoryManager();
  }
  cmm->push();
  d_topScope = new(cmm) Scope(this, cmm, 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 Context::pop()
{
  Scope* top = d_topScope;
  //  TRACE("context", "*** [context] Popping scope from level ", level(), "...");
  DebugAssert(top->prevScope() != NULL,
	      "Illegal to pop last scope off of stack.");
  // Notify before popping the scope
  for(vector<ContextNotifyObj*>::iterator i=d_notifyObjList.begin(),
	iend=d_notifyObjList.end(); i != iend; ++i)
    (*i)->notifyPre();
  // Pop the scope
  d_topScope = top->prevScope();
  top->restore();
  IF_DEBUG(top->check());
  ContextMemoryManager* cmm = top->getCMM();
  cmm->pop();
  d_cmmStack.push_back(cmm);

  // Notify after the pop is done
  for(vector<ContextNotifyObj*>::iterator i=d_notifyObjList.begin(),
	iend=d_notifyObjList.end(); i != iend; ++i)
    (*i)->notify();
  //  TRACE("context", "*** [context] Popped scope to level ", level(), "}");
  IF_DEBUG(
    string indentStr(level(), ' ');
    TRACE("pushpop", indentStr, "}", " Pop");
  )
}


void Context::popto(int toLevel)
{
  while (toLevel < topScope()->level()) pop();
}


void Context::deleteNotifyObj(ContextNotifyObj* obj) {
  size_t i(0), iend(d_notifyObjList.size());
  for(; i<iend && d_notifyObjList[i]!=obj; ++i);
  if(i<iend) { // Found obj; delete it from the vector
    d_notifyObjList[i]=d_notifyObjList.back();
    d_notifyObjList.pop_back();
  }
}


unsigned long Context::getMemory()
{
  Scope* scope = d_topScope;
  unsigned long memory = ContextMemoryManager::getStaticMemory();
  unsigned long mem = 0;
  cout << "Static: " << memory << endl;
  while (scope != NULL) {
    mem = scope->getCMM()->getMemory();
    cout << "Scope " << scope->level() << ": " << mem << endl;
    memory += mem;
    scope = scope->prevScope();
  }
  mem = 0;
  for (unsigned long i = 0; i < d_cmmStack.size(); ++i) {
    mem += d_cmmStack[i]->getMemory();
  }
  cout << "Stack: " << mem << endl;
  return memory + mem;
}


///////////////////////////////////////////////////////////////////////////////
// ContextManager methods                                                    //
///////////////////////////////////////////////////////////////////////////////


ContextManager::ContextManager()
{
  d_curContext = createContext("default");
}


ContextManager::~ContextManager()
{
  while (d_contexts.size()) {
    delete d_contexts.back();
    d_contexts.pop_back();
  }
}


Context* ContextManager::createContext(const string& name)
{
  d_contexts.push_back(new Context(this, name, d_contexts.size()));
  return d_contexts.back();
}


Context* ContextManager::switchContext(Context* context)
{
  FatalAssert(false, "Multiple contexts not yet implemented");
  Context* old = d_curContext;
  DebugAssert(context && context == d_contexts[context->id()],
	      "Unknown context");
  d_curContext = context;
  // TODO: Fix up all Context Objects
  return old;
}


unsigned long ContextManager::getMemory()
{
  return d_curContext->getMemory();
}


syntax highlighted by Code2HTML, v. 0.9.1