// file: $isip/class/search/SymbolGraphNode/sgrpn_05.cc
// version: $Id: sgrpn_05.cc 9373 2003-12-22 22:07:43Z alphonso $
//

// isip include files
//
#include "SymbolGraphNode.h"

// method: insertNode
//
// arguments:
//  SymbolGraphNode* node: (input) node to add to next node list
//  SymbolGraphNode** lnode: (input) array of symbol graph nodes
//  long*& map_list: (input / output) the map from the old graph to new
//
// return: a boolean indicating status
//
// this method adds the input node to the next list
//
boolean SymbolGraphNode::insertNode(SymbolGraphNode* node_a,
				    SymbolGraphNode** lnode_a,
				    long*& map_list_a) {

  // declare local variables
  //
  Float* val = (Float*)NULL;
  SymbolGraphNode* temp_node = (SymbolGraphNode*)NULL;
  
  float lm_score = 0.0;
  float ac_score = 0.0;
  
  // get list of possible next words from the lattice node
  //
  SingleLinkedList<SymbolGraphNode>& next_list = node_a->getNextNodesList();

  // get the corresponding transition scores
  //
  SingleLinkedList<Float>& lm_scores = node_a->getLMScoresList();
  SingleLinkedList<Float>& ac_scores = node_a->getScoresList();
  
  // loop through all the next words
  //
  lm_scores.gotoFirst();
  ac_scores.gotoFirst();
  
  for (boolean more = next_list.gotoFirst(); more;
       more = next_list.gotoNext()) {
    
    // set the language and acoustic scores
    //
    if ((val = lm_scores.getCurr()) == (Float*)NULL) {
      return Error::handle(name(), L"insertNextNode", Error::ARG, __FILE__, __LINE__);
    }
    lm_score = (float)(*val);
    
    val = (Float*)NULL;
    if ((val = ac_scores.getCurr()) == (Float*)NULL) {
      return Error::handle(name(), L"insertNextNode", Error::ARG, __FILE__, __LINE__);
    }      
    ac_score = (float)(*val);
    
    // get the symbol graph node
    //
    temp_node = next_list.getCurr();
    
    // add the nodes to the new symbol graph
    //	
    if (!insertNode(temp_node, lm_score, ac_score, lnode_a, map_list_a)) {
      return Error::handle(name(), L"insertNextNode", Error::ARG, __FILE__, __LINE__);
    }

    // move to the next transition
    //
    lm_scores.gotoNext();
    ac_scores.gotoNext();  
  }
  
  // exit gracefully
  //
  return true;    
}

// method: insertNode
//
// arguments:
//  SymbolGraphNode* node: (input) node to add to next node list
//  float lm_score: (input) the lm score associated with this transition
//  float ac_score: (input) the ac score associated with this transition
//  SymbolGraphNode** lnode: (input) array of symbol graph nodes
//  long*& map_list: (input / output) the map from the old graph to new
//
// return: a boolean indicating status
//
// this method adds the input node to the next list
//
boolean SymbolGraphNode::insertNode(SymbolGraphNode* node_a, float lm_score_a,
				   float ac_score_a, SymbolGraphNode** lnode_a,
				   long*& map_list_a) {

  // define local variables
  //
  boolean flag = false;
  boolean self_loop = false;

  String symbol;
  String next_symbol;  

  SymbolGraphNode* term_node = (SymbolGraphNode*)NULL;
  SymbolGraphNode* next_node = (SymbolGraphNode*)NULL;
  SymbolGraphNode* nxt_nd = (SymbolGraphNode*)NULL;

  // special case: when the input node is a term node
  //
  term_node = node_a->getParentGraph()->getTerm();
  if (node_a == term_node) {

    term_node = parent_graph_d->getTerm();
    if (!next_nodes_d.contains(term_node)) {
      insertNextNode(term_node, lm_score_a, ac_score_a);
    }
    
    // exit gracefully
    //
    return true;
  }
  
  // get the node index corresponding to the input node
  //
  long index = node_a->node_index_d;
    
  // get the symbol to be inserted in the new symbol graph
  //
  node_a->getSymbol(symbol);

  // if this reduced graph node has any next nodes
  //
  if (!next_nodes_d.isEmpty()) {

    // loop through all the next symbols for this reduced graph node
    //
    next_nodes_d.setMark();
    
    for (boolean more = next_nodes_d.gotoFirst(); more;
	 more = next_nodes_d.gotoNext()) {
      
      // get the current symbol graph node
      //
      next_node = next_nodes_d.getCurr();
      
      // check if a node corresponding to the input word already
      // exists in the compact lattice
      //
      next_node->getSymbol(next_symbol);
      
      if (symbol.eq(next_symbol)) {
	
	// check if this is creating a self loop in the future
	//
	self_loop = false;
	
	// get the next nodes of the input node
	//
	SingleLinkedList<SymbolGraphNode>& next_list =
	  node_a->getNextNodesList();
	
	if (!next_list.isEmpty()) {
	  
	  // loop over all next nodes
	  //	  
	  for (boolean more1 = next_list.gotoFirst();
	       more1; more1 = next_list.gotoNext()) {
	    
	    // get the next nodes
	    //
	    nxt_nd = next_list.getCurr();
	    
	    // check if this is mapped to the current next_node
	    //
	    if (next_node->node_index_d == map_list_a[nxt_nd->node_index_d]) {
	      self_loop = true;
	      break;
	    }
	  } // end for loop
	} // end if next nodes list is not null
	
	// now check if this node needs to be created or is already covered
	//
	if (self_loop == false) {	
	  
	  // check if the input node is already covered --- if not merge
	  // it with the current node
	  //
	  if (map_list_a[index] == (long)-1) {
	    
	    // update the map list for mapping from old lattice to the new
	    // one
	    //
	    map_list_a[index] = next_node->node_index_d;
	    
	    // move on to the next level of lattice nodes for the input
	    // node
	    //
	    next_node->insertNode(node_a, lnode_a, map_list_a);
	    
	    // set the flag and quit loop
	    //
	    flag = true;
	    break;
	  }
	  
	  // otherwise stop processing this trail here, as these two are
	  // the same node
	  //
	  else if (map_list_a[index] == next_node->node_index_d) {
	    
	    // set the flag and quit loop
	    //
	    flag = true;
	    break;
	  }
	} // end if this is no self loop
      } // end if word equals next_node word
    } // end for loop

    next_nodes_d.gotoMark();
    
  } // end if next_nodes_d is not NULL
  
  // otherwise there is no lattice node at this level containing the
  // current word
  //
  if (flag == false) {
    
    // if the node has not been created already
    //
    if (map_list_a[index] == (long)-1) {
      
      // create a new next node
      //
      next_node = parent_graph_d->insertNode(0, symbol);

      // set this as the new next node and add it to the current node
      //
      long node_count = next_node->getNodeIndex();
      lnode_a[node_count] = next_node;
      map_list_a[index] = node_count;

      insertNextNode(next_node, lm_score_a, ac_score_a);
      
      // get list of possible next words from the lattice node
      //
      next_node->insertNode(node_a, lnode_a, map_list_a);
    }
    
    // otherwise the node exists and there is no need to create a new
    // node, just point it to the corresponding existing instance
    //
    else {
      
      // get the next node and set up the arc
      //
      next_node = lnode_a[map_list_a[index]];

      insertNextNode(next_node, lm_score_a, ac_score_a);
    }
  }
  
  // exit gracefully
  //
  return true;    
}

// method: insertNextNode
//
// arguments:
//  SymbolGraphNode*& node: (input) node to be added to the next list
//  float lm_score: (input) language model score
//  float ac_score: (input) acoustic model score
//
// return: a boolean indicating status
//
// this method adds the input node to the next list
//
boolean SymbolGraphNode::insertNextNode(SymbolGraphNode*& node_a,
					float lm_score_a,
					float ac_score_a) {

  // declare local variables
  //
  Float lm_score(lm_score_a);
  Float ac_score(ac_score_a);
  
  // verify that the current node is  valid
  //
  if (node_a == (SymbolGraphNode*)NULL) {
    return Error::handle(name(), L"insertNextNode", Error::ARG, __FILE__, __LINE__);
  }

  // add the node to the next nodes list
  //
  next_nodes_d.insertLast(node_a);

  // add the corresponding language and acoustic model scores
  //
  lm_scores_d.insertLast(&lm_score);
  ac_scores_d.insertLast(&ac_score);

  parent_graph_d->incrementArcs();  
  num_arcs_out_d++;
  
  // exit gracefully
  //
  return true;  
}

// 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.
// 
boolean SymbolGraphNode::clear(Integral::CMODE cmode_a) {

  // clear the data members
  //
  frame_index_d = DEF_FRAME_INDEX;
  node_index_d = DEF_NODE_INDEX;  
  reference_count_d = DEF_REFERENCE_COUNT;  
  score_d = DEF_SCORE;

  num_arcs_in_d = DEF_IN_DEGREE;
  num_arcs_out_d = DEF_OUT_DEGREE;    

  // clear the lists
  //
  symbol_d.clear(cmode_a);    

  prev_nodes_d.clear(cmode_a);
  next_nodes_d.clear(cmode_a);

  prev_scores_d.clear(cmode_a);
  prevlm_scores_d.clear(cmode_a);
  
  lm_scores_d.clear(cmode_a);
  ac_scores_d.clear(cmode_a);  

  // exit gracefully
  //
  return true;
}

// method: getScores
//
// arguments:
//  SymbolGraphNode* node: (input) destination node
//  float& lm_score: (output) language model score
//  float& ac_score: (output) acoustic model score
//
// return: a boolean indicating status
//
// this method retrieves the language and acoustic mode score
//
boolean SymbolGraphNode::getScores(SymbolGraphNode* node_a,
				   float& lm_score_a,
				   float& ac_score_a) {

  // revert to the previous positions
  //
  next_nodes_d.setMark();
  lm_scores_d.setMark();
  ac_scores_d.setMark();
  
  // loop over all nodes adjacent to the this
  //
  lm_scores_d.gotoFirst();
  ac_scores_d.gotoFirst();
  
  for (boolean more = next_nodes_d.gotoFirst(); more;
       more = next_nodes_d.gotoNext()) {

    if (next_nodes_d.getCurr() == node_a) {
      lm_score_a = (float)(*lm_scores_d.getCurr());
      ac_score_a = (float)(*ac_scores_d.getCurr());
      break;
    }

    lm_scores_d.gotoNext();
    ac_scores_d.gotoNext();    
  }
  
  // revert to the previous positions
  //
  next_nodes_d.gotoMark();
  lm_scores_d.gotoMark();
  ac_scores_d.gotoMark();    
    
  // exit gracefully
  //
  return true;  
}

// method: insertPrevNode
//
// arguments:
//  SymbolGraphNode*& node: (input) node to be added to the previous list
//  float score: (input) path score
//  long max_hist: (input) maximum history limit
//  float lm_score: (input) language model score
//
// return: a boolean indicating status
//
// this method adds the input node to the previous list
//
boolean SymbolGraphNode::insertPrevNode(SymbolGraphNode*& node_a,
					float score_a,
					long max_hist_a,
					float lm_score_a) {

  // declare local variables
  //
  float score = 0.0;
  boolean inserted_node = false;

  Float ac_score(score_a);
  Float lm_score(lm_score_a);  

  SymbolGraphNode* node = (SymbolGraphNode*)NULL;
  SymbolGraphNode* tmp_ptr = (SymbolGraphNode*)NULL;
    
  // verify that the current node is  valid
  //
  if (node_a == (SymbolGraphNode*)NULL) {
    return Error::handle(name(), L"insertPrevNode", Error::ARG, __FILE__, __LINE__);
  }

  // is the current node already present in the previous list?
  //
  if (prev_nodes_d.find(node_a)) {

    if (prev_nodes_d.getCurr() != node_a) {
      return Error::handle(name(), L"insertPrevNode", Error::ARG, __FILE__, __LINE__);
    }
    
    // update score if necessary
    //
    inserted_node = true;

    long pos = prev_nodes_d.getPosition();
    prev_scores_d.gotoPosition(pos);
    prevlm_scores_d.gotoPosition(pos);    
    
    score = (float)(*prev_scores_d.getCurr());
      
    if (score_a > score) {
      prev_scores_d.getCurr()->assign(ac_score);
      prevlm_scores_d.getCurr()->assign(lm_score);      
    }
  }

  // loop over the previous list and compare
  //  
  else {
    
    boolean more_nodes = prev_nodes_d.gotoFirst() &
      prev_scores_d.gotoFirst() & prevlm_scores_d.gotoFirst();
    
    while (more_nodes) {

      node = prev_nodes_d.getCurr();      
      score = (float)(*prev_scores_d.getCurr());
            
      // insert the input node
      //
      if (score_a > score) {
	
	inserted_node = true;
	
	prev_nodes_d.insert(node_a, true);
	prev_scores_d.insert(&ac_score, true);
	prevlm_scores_d.insert(&lm_score, true);	
	
	node_a->incrementRefCount();
	parent_graph_d->incrementArcs();
	num_arcs_in_d++;
	
	break;
      }
      
      // any more nodes left?
      //
      more_nodes = prev_nodes_d.gotoNext() &
	prev_scores_d.gotoNext() & prevlm_scores_d.gotoNext();
    }
  }

  // make sure we inserted the node
  //
  if (!inserted_node && ((long)num_arcs_in_d < max_hist_a)) {
      
    prev_nodes_d.insertLast(node_a);
    prev_scores_d.insertLast(&ac_score);
    prevlm_scores_d.insertLast(&lm_score);    

    node_a->incrementRefCount();
    parent_graph_d->incrementArcs();
    num_arcs_in_d++;    
  }
  
  // when the length of the previous list exceeds the history limit we need
  // remove a node
  //
  if ((long)num_arcs_in_d > max_hist_a) {

    // remove the last node in the list
    //
    prev_nodes_d.removeLast(tmp_ptr);
    prev_scores_d.removeLast();
    prevlm_scores_d.removeLast();    

    tmp_ptr->decrementRefCount();
    parent_graph_d->decrementArcs();    
    num_arcs_in_d--;
  }

  // set the best score for the symbol node
  //
  if (!prev_scores_d.isEmpty()) {
    score_d = (float)(*prev_scores_d.getFirst());
  }
  
  // exit gracefully
  //
  return true;
}
