// file: latn_add_2.cc
//

// isip include files
//
#include "lattice_node.h"
#include "lattice_node_constants.h"

// method: add_node_cc
//
// arguments:
//  Lattice_node* lat_node : (input) lattice node to add to next node
//  float_4 lmscore : (input) the lm score associated with this transition
//  float_4 acscore : (input) the ac score associated with this transition
//  Lattice_node** lnode : (input) array of lattice nodes
//  int_4*& map_list : (input / output) the map from the old lattice to new
//  int_4& node_count : (input/output) the count of new lattice nodes
//  int_4& arc_count : (input/output) the count of new lattice arcs
//
// return: logical_1 indicating status
//
// this method adds a node to the next words list --- it is used when
// compacting a large lattice to a smaller form
//
logical_1 Lattice_node::add_node_cc(Lattice_node* lat_node_a,
				    float_4 lmscore_a,
				    float_4 acscore_a,
				    Lattice_node** lnode_a,
				    int_4*& map_list_a,
				    int_4& node_count_a,
				    int_4& arc_count_a) {
  
  // define local variables
  //
  logical_1 flag = ISIP_FALSE;
  logical_1 self_loop = ISIP_FALSE;
  int_4 index = lat_node_a->node_index_d;
  Lattice_node* next_node = (Lattice_node*)NULL;
  Lattice_node* nxt_nd = (Lattice_node*)NULL;
  Link_node* node = (Link_node*)NULL;
  Memory_manager* manager = Link_list::get_manager_cc();
  
  // word to be inserted in the new lattice
  //
  Word* word = lat_node_a->get_word_cc();

  // time frame of this node
  //
  int_4 frame = lat_node_a->get_frame_index_cc();

  // if this reduced lattice node has any next nodes
  //
  if (next_nodes_d != (Link_list*)NULL) {

    // loop through all the next words for this reduced lattice node
    //
    for (node = next_nodes_d->get_head_cc();
	 node != (Link_node*)NULL; node = node->get_next_cc()) {
      
      // get the lattice node here
      //
      next_node = (Lattice_node*)(node->get_item_cc());
      
      // check if a node corresponding to the input word already
      // exists in the compact lattice
      //
      if (word == next_node->word_d) {
	
	// check if this is creating a self loop in the future
	//
	self_loop = ISIP_FALSE;
	
	// get the next nodes of the input node
	//
	if (lat_node_a->next_nodes_d != (Link_list*)NULL) {
	  
	  // loop over all next nodes
	  //	  
	  for (Link_node* lnd = lat_node_a->next_nodes_d->get_head_cc();
	       lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	    
	    // get the next nodes
	    //
	    nxt_nd = (Lattice_node*)(lnd->get_item_cc());

	    // 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 = ISIP_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 == ISIP_FALSE) {	
	  
	  // check if the input node is already covered --- if not merge
	  // it with the current node
	  //
	  if (map_list_a[index] == (int_4)-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->add_node_cc(lat_node_a, lnode_a, map_list_a,
				   node_count_a, arc_count_a);
	    
	    // set the flag and quit loop
	    //
	    flag = ISIP_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 = ISIP_TRUE;
	    break;
	  }
	} // end if this is no self loop
      } // end if word equals next_node word
    } // end for loop
  } // end if next_nodes_d is not NULL

  // otherwise there is no lattice node at this level containing the
  // current word
  //
  if (flag == ISIP_FALSE) {
  
    // if the node has not been created already
    //
    if (map_list_a[index] == (int_4)-1) {
      
      // create a new next node
      //
      next_node = manager->new_lat_cc();
      next_node->word_d = word;
      next_node->node_index_d = node_count_a;	
      next_node->frame_d = frame;

      // set this as the new next node and add it to the current node
      //
      lnode_a[node_count_a] = next_node;
      map_list_a[index] = node_count_a;
      add_next_node_cc(next_node);
      next_node->add_prev_node_cc(this);
      add_lm_score_cc(lmscore_a);
      add_ac_score_cc(acscore_a);
      
      // increment count of nodes in the compact lattice
      //
      node_count_a++;
      arc_count_a++;
      
      // get list of possible next words from the lattice node
      //
      next_node->add_node_cc(lat_node_a, lnode_a, map_list_a,
			     node_count_a, arc_count_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]];
      add_next_node_cc(next_node);
      next_node->add_prev_node_cc(this);
      add_lm_score_cc(lmscore_a);
      add_ac_score_cc(acscore_a);
      
      // increment the arcs count
      //
      arc_count_a++;
    }
  }

  // exit gracefully
  //
  return ISIP_TRUE;
}
