// file: $isip/class/search/Context/Context.h
// version: $Id: Context.h 9042 2003-03-12 17:35:25Z huang $
//

// make sure definitions are only made once
//
#ifndef ISIP_CONTEXT
#define ISIP_CONTEXT

// isip include files
//
#ifndef ISIP_GRAPH_VERTEX
#include <GraphVertex.h>
#endif

#ifndef ISIP_CIRCULAR_DELAY_LINE
#include <CircularDelayLine.h>
#endif

#ifndef ISIP_SEARCH_NODE
#include <SearchNode.h>
#endif

// forward class definition(s):
//
class SearchNode;

// Context: a class to keep the context through a search
//  it contain a circular delay line of pointers to GraphVertex<SearchNode>
//  these pointers stored in the form of Ulong
//  since we need to generate a hash table that maps all accounted
//  contexts to subgraphs at the next lower level, we need to provide
//  a hashing function here
//
class Context : public CircularDelayLine<Ulong> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------
  
  static const String PARAM_CENTRAL;
  static const String PARAM_EXTENSION_LENGTH;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // default values
  //
  static const long DEF_LENGTH = 1;
  static const long DEF_CENTRAL_INDEX = 1;
  static const long DEF_LAST_LENGTH = -1;    
  static const long DEF_EXTENSION_LENGTH = 0;  
  static const ulong DEF_HASH_INDEX = 0;  
  
  // define default arguments to methods
  //
  static const ulong DEF_REF_INCR = 1;
  static const ulong DEF_REF_DECR = 1;  

  //---------------------------------------
  //
  // error codes
  //
  //---------------------------------------

  static const long ERR = 90100;

  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  // index of the central vertex in context
  //
  long central_d;

  // length of the future context extension, used if we have a
  // cross-symbol context
  //
  long ext_length_d;

  // static debug level
  //
  static Integral::DEBUG debug_level_d;

  // static memory manager
  //
  static MemoryManager mgr_d;

  // type definition
  // 
  typedef GraphVertex<SearchNode> GVSnode;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }

  // other static methods
  //
  static boolean diagnose(Integral::DEBUG debug_level);

  // the debug method is inherited from the Stack class
  //
  
  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG debug_level) {
    debug_level_d = debug_level;
    return true;
  }

  // method: destructor
  //
  ~Context() {
    if (debug_level_d >= Integral::ALL) {     
      fprintf(stdout, "Destructor of context: %p\n", this);
      fflush(stdout);
    }
  }

  // method: default constructor
  //
  Context(long context_length = DEF_LENGTH, long central_index
	  = DEF_CENTRAL_INDEX) : CircularDelayLine<Ulong>(context_length) {

    if (debug_level_d >= Integral::ALL) { 
      fprintf(stdout, "Constructor of context: %p\n", this);
      fflush(stdout);
    }
    
    central_d = central_index;
    ext_length_d = DEF_EXTENSION_LENGTH;
  }

  // method: copy constructor
  //
  Context(const Context& copy_context) :
    CircularDelayLine<Ulong>(copy_context.length()) {

    if (debug_level_d >= Integral::ALL) { 
      fprintf(stdout, "Constructor of context: %p\n", this);
      fflush(stdout);
    }

    assign(copy_context);
  }

  // assign method
  //
  boolean assign(const Context& context_cmp);  

  // equality method
  //
  boolean eq(const Context& context_cmp) const;

  // method: sofSize
  //
  long sofSize() const;

  // method: read
  //
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME);
  
  // method: write
  //
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME) const;

  // method: readData
  //
  boolean readData(Sof& sof, const String& pname = String::EMPTY,
		   long size = SofParser::FULL_OBJECT, boolean param = true,
                   boolean nested = false);

  // method: writeData
  //
  boolean writeData(Sof& sof, const String& pname = String::EMPTY) const;

  // method: new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

  // method: new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }

  // method: delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }

  // method: delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }

  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }

  // method: clear
  //
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE) {

    // call the vector clear
    //
    v_d.clear(cmode);
    
    // clear the index in any case
    //
    index_d = DEF_INDEX;
    ext_length_d = DEF_EXTENSION_LENGTH;    

    return true;
  }  

  //---------------------------------------------------------------------------
  //
  // class-specific public methods
  //
  //---------------------------------------------------------------------------

  // method: getCentralLength
  //
  long getCentralLength() const {
    return central_d;
  }

  // method: setCentralLength
  //
  boolean setCentralLength(long arg) {
    return (central_d = arg);
  }  

  // method: getExtendedLength
  //
  long getExtendedLength() const {
    return ext_length_d;
  }

  // method: setExtendedLength
  //
  boolean setExtendedLength(long arg) {
    return (ext_length_d = arg);
  }    
    
  // method: isExtended
  //
  boolean isExtended() {
    return (ext_length_d > 0);
  }
  
  // method: getCentralVertex
  //
  GVSnode* getCentralVertex() {
    ulong pointer_to_central = (*this)(-central_d - ext_length_d);
    return (GVSnode*) pointer_to_central;
  }

  // method: setCentralVertex
  //
  boolean setCentralVertex(ulong new_gvnode_a) {
    (*this)(-central_d - ext_length_d) = new_gvnode_a;
    return true;
  }
  
  // method: getBeforeCentralVertex
  //
  GVSnode* getBeforeCentralVertex() {
    ulong pointer_to_vertex = (*this)(-central_d - ext_length_d - 1);
    return (GVSnode*) pointer_to_vertex;
  }

  // method: setBeforeCentralVertex
  //
  boolean setBeforeCentralVertex(ulong new_gvnode_a) {
    (*this)(-central_d - ext_length_d - 1 ) = new_gvnode_a;
    return true;
  }
  
  // method: getAfterCentralVertex
  //
  GVSnode* getAfterCentralVertex() {
    ulong pointer_to_vertex = (*this)(-central_d - ext_length_d + 1);
    return (GVSnode*) pointer_to_vertex;
  }  
  
  // method: getLastVertex
  //
  GVSnode* getLastVertex() {
    ulong pointer_to_last = (*this)(DEF_LAST_LENGTH);
    return (GVSnode*) pointer_to_last;
  }
  
  // method: setLastVertex
  //
  boolean setLastVertex(ulong new_gvnode_a) {
    (*this)(DEF_LAST_LENGTH) = new_gvnode_a;
    return true;
  }
  
  // method to print the context
  //
  boolean print();
  boolean print(String& output);  
    
  // method to convert GraphVertex<SearchNode> pointers to SearchSymbol
  //  indices (non-reversible)
  //
  //  conversion needs to be done before hashing, because key to the
  //  hash table is the array of search indices, not array of pointers
  //  to graph vertices
  //
  boolean convert();  
  boolean convert(Context*& output, boolean extended = false);

  // methods to extend the context
  //
  boolean fold();
  boolean extend(GVSnode* pointer_to_vertex);

  // method: getHashIndex
  //
  long getHashIndex() {
    ulong hash_index = (ulong)0;
    for (long i = v_d.length(); i > 0; i--) {
      ulong index = (ulong)(*this)(-i);
      hash_index = (hash_index << 5) ^ (hash_index >> 27) ^ index;
    }
    return hash_index;
  }
  
  // hash function
  //
  long hash(long capacity) const;

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  core data manipulation methods
  //
  //---------------------------------------------------------------------------
 
  // method: advanceAndAssign
  //
  boolean advanceAndAssign(const Ulong& input,
			   long increment = DEF_INCREMENT) {
    boolean status = advance(increment);
    v_d(index_d) = input;
    return status;
  }
  
  // method: assignAndAdvance
  //
  boolean assignAndAdvance(const Ulong& input,
			   long increment = DEF_INCREMENT) {
    v_d(index_d) = input;
    boolean status = advance(increment);
    return status;
  }
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

};

// end of include file
//
#endif
