// file: $isip/class/dstr/GraphVertex/GraphVertex.h
// version: $Id: GraphVertex.h 10636 2007-01-26 22:18:09Z tm334 $
//

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

// isip include files
//
#ifndef ISIP_SINGLE_LINKED_LIST
#include <SingleLinkedList.h>
#endif

#ifndef ISIP_MEMORY_MANAGER
#include <MemoryManager.h>
#endif

#ifndef ISIP_STRING
#include <String.h>
#endif

// forward class definitions:
//  we must define the Graph class and the GraphArc class here first because
//  the header files might be short-circuited by the ifndef.
//
template<class TObject> class DiGraph;
template<class TObject> class GraphArc;

// GraphVertex: a generic GraphVertex. the main purpose of a vertex is
// to hold a list of arcs originating from the given vertex.
//
template<class TObject>
class GraphVertex : public SingleLinkedList<GraphArc<TObject> >,
		    public Node<TObject> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;

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

  // default values
  //
  
  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 41100;
  static const long ERR_NULLD = 41101;
  static const long ERR_NOPAR = 41102;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // what graph does this vertex belong to
  //
  DiGraph<TObject>* parent_graph_d;

  // define the memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // static methods
  //
  static const String& name();

  // this class doesn't have a diagnose method, its functionality is
  // tested in Graph::diagnose
  //
  
  // debug methods:
  //  setDebug is inherited from the SingleLinkedList template class
  //
  boolean debug(const unichar* message) const;
  
  // method: destructor
  //
  ~GraphVertex() {
    removeAllArcs();
  }

  // method: default constructor
  //  
  GraphVertex() {
    parent_graph_d = (DiGraph<TObject>*)NULL;
    SingleLinkedList< GraphArc<TObject> >::setAllocationMode(DstrBase::USER);
  }

  // method: copy constructor
  //  
  GraphVertex(const GraphVertex<TObject>& arg) {
    parent_graph_d = (DiGraph<TObject>*)NULL;
    SingleLinkedList< GraphArc<TObject> >::setAllocationMode(DstrBase::USER);
    assign(arg);
  }

  // assign methods
  //
  boolean assign(const GraphVertex<TObject>& copy_vertex);

  // method: operator=
  //
  GraphVertex<TObject>& operator=(const GraphVertex<TObject>& arg) {
    assign(arg);
    return *this;
  }

  // i/o methods:
  //  these methods are not needed since graph takes care of it's own I/O.
  //
  
  // method: eq
  //  determines if the contents of the vertex are equivalent.
  //  adjacency lists & weights are ignored.
  //
  boolean eq(const GraphVertex<TObject>& arg) const {
    return Node<TObject>::eq(arg);
  }
  
  // method: new
  //
  void* operator new(size_t size) {
    return mgr_d.get();
  }

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

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

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

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

  // other memory management methods:
  //
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  parent graph manipulation methods
  //
  //---------------------------------------------------------------------------

  // method: setParentGraph
  //
  boolean setParentGraph(DiGraph<TObject>* parent) {
    parent_graph_d = parent;
    return true;
  }

  // method: getParentGraph
  //
  DiGraph<TObject>* getParentGraph() const {
    return parent_graph_d;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  vertex manipulation methods
  //
  //---------------------------------------------------------------------------
  
  // insert/remove a vertex to/from the adjacency list:
  //  these methods assume that the user is taking care of updating the
  //  parent graphs, end vertices, etc.
  //
  boolean insertArc(GraphVertex<TObject>* vertex,
		    double weight = GraphArc<TObject>::DEF_WEIGHT,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON);
  boolean insertArc(GraphVertex<TObject>* vertex,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON);
  
  boolean insertArc(GraphVertex<TObject>* vertex,
		    GraphArc<TObject>*& new_arc,		    
		    double weight = GraphArc<TObject>::DEF_WEIGHT,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON);  
  boolean insertArc(GraphVertex<TObject>* vertex,
		    GraphArc<TObject>*& new_arc,
		    boolean is_epsilon = GraphArc<TObject>::DEF_EPSILON);  
  
  boolean removeArc(GraphVertex<TObject>* vertex);
  boolean removeArc();
  boolean removeAllArcs();
  
  // is this vertex adjacent?
  //
  boolean isAdjacent(GraphVertex<TObject>* vertex) const;

  // method: isStart
  //
  boolean isStart() const {
    if (parent_graph_d == (DiGraph<TObject>*)NULL) {
      return Error::handle(name(), L"isStart - undefined parent", Error::ARG, __FILE__, __LINE__);
    }
    return (this == parent_graph_d->getStart());
  }

  // method: isTerm
  //
  boolean isTerm() const {
    if (parent_graph_d == (DiGraph<TObject>*)NULL) {
      return Error::handle(name(), L"isTerm - undefined parent", Error::ARG, __FILE__, __LINE__);
    }
    return (this == parent_graph_d->getTerm());
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  vertex comparison methods
  //
  //---------------------------------------------------------------------------

  // compares the siblings of the two vertices and arc weights to see if
  // the two vertices has a similar structure
  //
  boolean compareVertices(const GraphVertex<TObject>& compare_vertex_a) const;

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

  // friend class
  //
  template <class TObject_diagnose> 
  friend class GraphVertexDiagnose;
};

// in order to allow graph to reference GraphVertex's constants, it
// must be included after the class definition.
//
#ifndef ISIP_DI_GRAPH
#include <DiGraph.h>
#endif

#ifndef ISIP_GRAPH_ARC
#include <GraphArc.h>
#endif

//-----------------------------------------------------------------------------
//
// we define non-integral constants at the end of class definition for
// templates (for non-templates these are defined in the default constructor)
//      
//-----------------------------------------------------------------------------

// constants: required constants such as the class name
//
template <class TObject>
const String GraphVertex<TObject>::CLASS_NAME(L"GraphVertex");

template <class TObject>
const String GraphVertex<TObject>::DEF_PARAM(L"vertex");

// static instantiations: the memory manager
//
template <class TObject>
MemoryManager GraphVertex<TObject>::mgr_d(sizeof(GraphVertex<TObject>),
					  CLASS_NAME);

// below are all the methods for the GraphVertex template class
//      

// ---------------------------------------------------------------------
//
// required static methods
//
//----------------------------------------------------------------------

// method: name
//
// arguments: none
//
// return: a static String& containing the class name
//
// this method returns the class name
//
template<class TObject>
const String& GraphVertex<TObject>::name() {

  // create the static name string for this class and return it
  //
  static String cname(CLASS_NAME);
  cname.clear();
  cname.concat(CLASS_NAME);
  cname.concat(L"<");
  cname.concat(TObject::name());
  cname.concat(L">");

  // return the name
  //
  return cname;
}

// ---------------------------------------------------------------------
//
// required debug methods
//
//----------------------------------------------------------------------

// method: debug
//
// arguments:
//  const unichar* message: (input) information message
//
// return: a boolean value indicating status
//
// this method dumps the contents of an object to the console
// 
template<class TObject>
boolean GraphVertex<TObject>::debug(const unichar* message_a) const {

  // build a debug string
  //
  String output;
  String value;
  output.debugStr(name(), message_a, L"");
  Console::put(output);
  
  Console::increaseIndention();

  // dump a pointer to the current vertex
  //
  value.assign(this);
  output.debugStr(name(), message_a, L"this", value);
  Console::put(output);
  
  // dump a pointer to the parent graph
  //
  value.assign(parent_graph_d);
  output.debugStr(name(), message_a, L"parent_graph_d", value);
  Console::put(output);

  // dump the internal item if it exists
  //
  Node<TObject>::debug(L"item");

  // call the parent debug method to debug the list itself
  //
  SingleLinkedList< GraphArc<TObject> >::debug(L"arcs");

  // that's it for sub-items, decrease indentation
  //
  Console::decreaseIndention();

  // exit gracefully
  // 
  return true;
}

//------------------------------------------------------------------------
//
//  required assign methods
//
//-------------------------------------------------------------------------

// method: assign
//
// arguments:
//  const GraphVertex<TObject>& arg: (input) the vertex to copy
//
// return: a boolean value indicating status
//
// this method copies the contents of the input to this list
//
template<class TObject>
boolean GraphVertex<TObject>::assign(const GraphVertex<TObject>& arg_a) {

  // it may be necessary to clear this vertex
  //
  clear();
  
  // call the parent assign method which handles the arc list elements
  //
  SingleLinkedList< GraphArc<TObject> >::assign(arg_a);

  // call the Node assign method to assign the object
  //
  Node<TObject>::assign(arg_a);
  
  // set the parent graph
  //
  setParentGraph(arg_a.parent_graph_d);

  // exit gracefully
  //
  return true;
}

//-------------------------------------------------------------------------
//
//  required clear method
//
//-------------------------------------------------------------------------

// method: clear
//
// arguments:
//  Integral::CMODE cmode_a: (input) clear mode
//
// return: a boolean value indicating status
//
// this method clears the reference to the internal data.
// 
template<class TObject>
boolean GraphVertex<TObject>::clear(Integral::CMODE cmode_a) {

  // switch on cmode
  //
  if (cmode_a == Integral::RETAIN) {

    if (this->item_d != (TObject*)NULL) {
      this->item_d->clear(cmode_a);
    }
  }

  else if ((cmode_a == Integral::RESET) || (cmode_a == Integral::RELEASE)) {
          
    // clear the references to the data
    //
    this->item_d = (TObject*)NULL;

    // clear the reference to the parent graph
    //
    parent_graph_d = (DiGraph<TObject>*)NULL;    
  }
  
  else {
    
    // deallocate the allocated memory if necessary
    //
    if (this->item_d != (TObject*)NULL) {
      this->item_d->clear(Integral::FREE);
      delete this->item_d;
      this->item_d = (TObject*)NULL;
    }

    // clear the reference to the parent graph
    //
    parent_graph_d = (DiGraph<TObject>*)NULL;
  }
  
  // clear the arc list
  //
  return removeAllArcs();
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  vertex manipulation methods
//
//---------------------------------------------------------------------------
  
// method: insertArc
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to connect to
//  double weight: (input) the weight on the generated arc
//  boolean is_epsilon: (input) is this an epsilon transition?
//
// return: a boolean value indicating status
//
// this method inserts a weighted arc connecting this vertex to the input
// vertex.
//
template<class TObject>
boolean GraphVertex<TObject>::insertArc(GraphVertex<TObject>* vertex_a,
				     double weight_a, boolean is_epsilon_a) {

  // check that the input vertex is not null
  //
  if (vertex_a == (GraphVertex<TObject>*)NULL) {
    return Error::handle(name(), L"insertArc", Error::ARG,
			 __FILE__, __LINE__);
  }

  // create a new Arc
  //
  GraphArc<TObject>* new_arc = new GraphArc<TObject>(vertex_a, weight_a);
  new_arc->setEpsilon(is_epsilon_a);

  // insert the arc to the list
  //
  insert(new_arc);

  // exit gracefully
  //
  return true;
}

// method: insertArc
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to connect to
//  boolean is_epsilon: (input) is this an epsilon transition?
//
// return: a boolean value indicating status
//
// this method inserts an arc connecting this vertex to the input
// vertex.
//
template<class TObject>
boolean GraphVertex<TObject>::insertArc(GraphVertex<TObject>* vertex_a,
				     boolean is_epsilon_a) {

  // check that the input vertex is not null
  //
  if (vertex_a == (GraphVertex<TObject>*)NULL) {
    return Error::handle(name(), L"insertArc", Error::ARG, __FILE__,
                         __LINE__);
  }

  // create a new Arc
  //
  GraphArc<TObject>* new_arc = new GraphArc<TObject>(vertex_a);
  new_arc->setEpsilon(is_epsilon_a);

  // insert the arc to the list
  //
  insert(new_arc);

  // exit gracefully
  //
  return true;
}
  
// method: insertArc
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to connect to
//  GraphArc<TObject>*& new_arc: (output) new created arc
//  double weight: (input) the weight on the generated arc
//  boolean is_epsilon: (input) is this an epsilon transition?
//
// return: a boolean value indicating status
//
// this method inserts a weighted arc connecting this vertex to the input
// vertex.
//
template<class TObject>
boolean GraphVertex<TObject>::insertArc(GraphVertex<TObject>* vertex_a,
					GraphArc<TObject>*& new_arc_a,
				     double weight_a, boolean is_epsilon_a) {

  // check that the input vertex is not null
  //
  if (vertex_a == (GraphVertex<TObject>*)NULL) {
    return Error::handle(name(), L"insertArc", Error::ARG,
			 __FILE__, __LINE__);
  }

  // create a new Arc
  //
  new_arc_a = new GraphArc<TObject>(vertex_a, weight_a);
  new_arc_a->setEpsilon(is_epsilon_a);

  // insert the arc to the list
  //
  insert(new_arc_a);

  // exit gracefully
  //
  return true;
}

// method: insertArc
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to connect to
//  GraphArc<TObject>*& new_arc: (output) new created arc
//  boolean is_epsilon: (input) is this an epsilon transition?
//
// return: a boolean value indicating status
//
// this method inserts an arc connecting this vertex to the input
// vertex.
//
template<class TObject>
boolean GraphVertex<TObject>::insertArc(GraphVertex<TObject>* vertex_a,	
					GraphArc<TObject>*& new_arc_a,
				     boolean is_epsilon_a) {

  // check that the input vertex is not null
  //
  if (vertex_a == (GraphVertex<TObject>*)NULL) {
    return Error::handle(name(), L"insertArc", Error::ARG, __FILE__,
                         __LINE__);
  }

  // create a new Arc
  //
  new_arc_a = new GraphArc<TObject>(vertex_a);
  new_arc_a->setEpsilon(is_epsilon_a);

  // insert the arc to the list
  //
  insert(new_arc_a);

  // exit gracefully
  //
  return true;
}

// method: removeArc
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to unlink from
//
// return: a boolean value indicating status
//
// this method unlinks this vertex from the input vertex. if the graph is
// not directed, then an arc from the input vertex is removed as well
//
template<class TObject>
boolean GraphVertex<TObject>::removeArc(GraphVertex<TObject>* vertex_a) {

  // make sure the list is not empty
  //
  if (this->isEmpty()) {
    return false;
  }
  
  // find the input vertex in the adjacency list by looping over all arcs
  //
  SingleLinkedNode< GraphArc<TObject> >* tmp_node = this->first_d;

  // declare temporary objects
  //
  GraphVertex<TObject>* tmp_vert = (GraphVertex<TObject>*) NULL;

  // loop over each element and look for the input vertex
  //
  boolean vertex_found = false;
  while ((tmp_node != (SingleLinkedNode< GraphArc<TObject> >*)NULL) &&
	 !vertex_found) {

    // get the vertex out of the arc
    //
    tmp_vert = tmp_node->getItem()->getVertex();

    // check to see if the found vertex is the one we are looking for
    //
    if (tmp_vert == vertex_a) {
      vertex_found = true;
    }
    else {
      tmp_node = tmp_node->getNext();
    }
  }

  // if we found the vertex then break the arcs, else return an error.
  //
  if (vertex_found) {

    // if the found vertex was at the current node position then remove the
    // node
    //
    if (this->curr_d == tmp_node) {

      // remove the arc from the graph
      //
      boolean status = false;
      GraphArc<TObject>* graph_arc;
      status = this->remove(graph_arc);
      delete graph_arc;

      if (!status) {
	return false;
      }
    }
    else {

      // put the current node pointer in place so we can call a
      // SingleLinkedList remove method
      //
      SingleLinkedNode< GraphArc<TObject> >* tmp_curr = this->curr_d;
      this->curr_d = tmp_node;

      // remove the node
      //
      boolean status = false;
      GraphArc<TObject>* graph_arc;
      status = this->remove(graph_arc);
      delete graph_arc;
      
      if (!status) {
	return false;
      }

      // put the current pointer back in place
      //
      this->curr_d = tmp_curr;
    }
  }
  else {
    return false;
  }

  // exit gracefully
  //
  return true;
}

// method: removeArc
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method unlinks this vertex from the vertex linked by the current arc
// in the list. if the graph is not directed, then an arc from the found
// vertex is removed as well
//
template<class TObject>
boolean GraphVertex<TObject>::removeArc() {

  // if the adjacency list is empty then return false
  //
  if (this->isEmpty()) {
    return false;
  }

  // remove this arc
  //
  boolean status = false;
  GraphArc<TObject>* graph_arc;  
  status = this->remove(graph_arc);
  delete graph_arc;
  
  // exit gracefully
  //
  return status;
}

// method: removeAllArcs
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method removes all arcs extending from this vertex
//
template<class TObject>
boolean GraphVertex<TObject>::removeAllArcs() {

  // declare a return value
  //
  boolean return_val = true;

  // loop until no arcs remain
  //
  while (!this->isEmpty()) {
    
    // remove the forward link
    //
    boolean status = false;
    GraphArc<TObject>* graph_arc;
    status = this->remove(graph_arc);
    delete graph_arc;
    
    // exit gracefully
    //
    return_val &= status;    
  }

  // exit gracefully
  //
  return return_val;
}

// method: isAdjacent
//
// arguments:
//  GraphVertex<TObject>* vertex: (input) the vertex to find
//
// return: boolean flag indicating whether or not the vertex was found
//
// this method finds the input vertex in the adjacency list for this
// vertex
//
template<class TObject>
boolean GraphVertex<TObject>::isAdjacent(GraphVertex<TObject>*
					 vertex_a) const {

  // make sure the list is not empty
  //
  if (this->isEmpty()) {
    return false;
  }
  
  // find the input vertex in the adjacency list by looping over all arcs
  //
  SingleLinkedNode< GraphArc<TObject> >* tmp_node = this->first_d;

  // declare temporary objects
  //
  GraphVertex<TObject>* tmp_vert = (GraphVertex<TObject>*) NULL;

  // loop over each element and look for the input vertex
  //
  boolean vertex_found = false;
  while ((tmp_node != (SingleLinkedNode< GraphArc<TObject> >*)NULL) &&
	 !vertex_found) {

    // get the vertex out of the node
    //
    tmp_vert = tmp_node->getItem()->getVertex();

    // check to see if the found vertex is the one we are looking for
    //
    if (tmp_vert == vertex_a) {
      vertex_found = true;
    }
    else {
      tmp_node = tmp_node->getNext();
    }
  }

  // exit gracefully
  //
  return vertex_found;
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  vertex comparison methods
//
//---------------------------------------------------------------------------

// method: compareVertices
//
// arguments:
//  const GraphVertex<TObject>& compare_vertex: (input) the vertex to compare
//
// return: boolean value indicating test of equivalence
//
// this method compares two vertices for equivalent structure
//
template<class TObject>
boolean GraphVertex<TObject>::compareVertices(const GraphVertex<TObject>&
					      vertex_a) const {

  // declare the output variable
  //
  boolean arc_equal = false;
  
  // check for null items in either vertex
  //
  if (this->item_d == (TObject*)NULL) {
    if (vertex_a.item_d != (TObject*)NULL) {
      return false;
    }
  }
  else if (vertex_a.item_d == (TObject*)NULL) {
    return false;
  }

  // make sure that the vertices have the same number of arcs
  //
  if (this->length() != vertex_a.length()) {

      // if the lengths are different then the number of arcs in the
      // vertex are different
      //
      return false;    
  }
  
  // save the current states
  //
  SingleLinkedNode< GraphArc<TObject> >* temp_curr1 = this->curr_d;
  SingleLinkedNode< GraphArc<TObject> >* temp_curr2 = vertex_a.curr_d;  

  // iterate over all arcs contained in the current vertex
  //
  boolean more_nodes = const_cast< GraphVertex<TObject>* >(this)->gotoFirst();
    
  while (more_nodes) {

    // get the current arc associated with the vertex
    //
    arc_equal = false;
    GraphArc<TObject>* this_arc = const_cast< GraphVertex<TObject>* >
      (this)->getCurr();

    // iterate over all  arcs associated with the input vertex
    //
    boolean more_nodes1 =
      const_cast< GraphVertex<TObject>& >(vertex_a).gotoFirst();

    while (more_nodes1) {

      // get the current arc associated with the vertex
      //
      GraphArc<TObject>* input_arc = const_cast< GraphVertex<TObject>& >
	(vertex_a).getCurr();

      // check if the arcs are similar
      //
      if (this_arc->eq(*input_arc)) {
	arc_equal = true;
      }

      // get the next arc in the vertex
      //
      more_nodes1 = const_cast< GraphVertex<TObject>& >(vertex_a).gotoNext();
    }

    // if none of the arcs in the input vector are similar exit
    //
    if (!arc_equal) {
      const_cast< GraphVertex<TObject>* >(this)->curr_d = temp_curr1;
      const_cast< GraphVertex<TObject>& >(vertex_a).curr_d = temp_curr2;    
      return false;
    }
    
    // get the next arc in the vertex
    //
    more_nodes = const_cast< GraphVertex<TObject>* >(this)->gotoNext();
  }
  
  // reset the current pointer and return true
  //
  const_cast< GraphVertex<TObject>* >(this)->curr_d = temp_curr1;
  const_cast< GraphVertex<TObject>& >(vertex_a).curr_d = temp_curr2;    
  return true;
}

// end of include file
//
#endif


