// file: tr_latn_add_2.cc
//

// isip include files
//
#include "train_lattice_node.h"
#include "train_lattice_node_constants.h"

// method: add_node_cc
//
// arguments:
//  Train_Lattice_node* lat_node : (input) lattice node to add to next node
//  float_8 lmscore : (input) the lm score associated with this transition
//  float_8 acscore : (input) the ac score associated with this transition
//  Train_Lattice_node** lnode : (input) array of lattice nodes
//  int_4*& map_list : (input / output) the map from the old lattice to new
//  int_4& count : (input/output) the count of new lattice nodes
//
// 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 Train_Lattice_node::add_node_cc(Train_Lattice_node* lat_node_a,
				    float_8 lmscore_a, float_8 acscore_a,
				    Train_Lattice_node** lnode_a,
				    int_4*& map_list_a, int_4& count_a) {
  
  // define local variables
  //
  logical_1 flag = ISIP_FALSE;
  int_4 cnt = (int_4)0;
  float_8 acscore = (float_8)0;
  float_8 lmscore = (float_8)0;
  float_8* lscore = (float_8*)NULL;
  float_8* ascore = (float_8*)NULL;
  Train_Lattice_node* next_node = (Train_Lattice_node*)NULL;
  Train_Lattice_node* temp_node = (Train_Lattice_node*)NULL;
  Train_Link_list* next_list = (Train_Link_list*)NULL;
  Train_Link_node* node = (Train_Link_node*)NULL;
  Train_Memory_manager* manager = Train_Link_list::get_manager_cc();
  
  // word to be inserted in the new lattice
  //
  Train_Word* word = lat_node_a->get_word_cc();

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

    // loop through all the next words for this reduced lattice node
    //
    for (node = next_nodes_d->get_head_cc();
	 node != (Train_Link_node*)NULL; node = node->get_next_cc()) {
      
      // get the lattice node here
      //
      next_node = (Train_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 the input node is already covered --- if not merge
	// it with the current node
	//
	if (map_list_a[lat_node_a->node_index_d] == (int_4)-1) {
	  
	  // update the map list for mapping from old lattice to the new
	  // one
	  //
	  map_list_a[lat_node_a->node_index_d] = next_node->node_index_d;
	  
	  // move on to the next level of lattice nodes for the input
	  // node
	  //
	  lscore = lat_node_a->get_lm_scores_cc();
	  ascore = lat_node_a->get_ac_scores_cc();

	  // get list of possible next words from the lattice node
	  //
	  next_list = lat_node_a->next_nodes_d;
	  if (next_list != (Train_Link_list*)NULL) {
	  
	    // loop through all the next words
	    //
	    for (node = next_list->get_head_cc(), cnt = 0;
		 node != (Train_Link_node*)NULL;
		 node = node->get_next_cc(), cnt++) {

	      // if no lm scores found in the original lattice
	      //
	      if (lscore == (float_8*)NULL) {
		lmscore = 0;
	      }
	      else {
		lmscore = lscore[cnt];
	      }
	      
	      // if no acoustic scores found in the original lattice
	      //
	      if (ascore == (float_8*)NULL) {
		acscore = 0;
	      }
	      else {
		acscore = ascore[cnt];
	      }
	      
	      // get the lattice node
	      //
	      temp_node = (Train_Lattice_node*)(node->get_item_cc());
	      
	      // add these nodes to the new lattice
	      //	
	      next_node->add_node_cc(temp_node, lmscore, acscore,
				     lnode_a, map_list_a, count_a);
	    }
	  }
	  
	  // set the flag and quit loop
	  //
	  flag = ISIP_TRUE;
	  break;
	}
	
	else if (map_list_a[lat_node_a->node_index_d] ==
		 next_node->node_index_d) {
	  
	  // set the flag and quit loop
	  //
	  flag = ISIP_TRUE;
	  break;
	}
      } // 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[lat_node_a->node_index_d] == (int_4)-1) {
      
      // create a new next node
      //
      lnode_a[count_a] = manager->new_lat_cc();
      lnode_a[count_a]->word_d = word;
      lnode_a[count_a]->node_index_d = count_a;	
      map_list_a[lat_node_a->node_index_d] = count_a;

      // set this as the new next node and add it to the current node
      //
      next_node = lnode_a[count_a];
      add_next_node_cc(next_node);
      add_lm_score_cc(lmscore_a);
      next_node->add_prev_node_cc(this);

      // increment count of nodes in the compact lattice
      //
      count_a++;
      
      // get list of possible next words from the lattice node
      //
      lscore = lat_node_a->get_lm_scores_cc();
      ascore = lat_node_a->get_ac_scores_cc();
      next_list = lat_node_a->next_nodes_d;
      
      // loop through all the next words
      //
      if (next_list != (Train_Link_list*)NULL) {
	for (node = next_list->get_head_cc(), cnt = 0;
	     node != (Train_Link_node*)NULL;
	     node = node->get_next_cc(), cnt++) {

	  // if no lm scores found in the original lattice
	  //
	  if (lscore == (float_8*)NULL) {
	    lmscore = 0;
	  }
	  else {
	    lmscore = lscore[cnt];
	  }
	  
	  // if no acoustic scores found in the original lattice
	  //
	  if (ascore == (float_8*)NULL) {
	    acscore = 0;
	  }
	  else {
	    acscore = ascore[cnt];
	  }

	  // get the lattice node
	  //
	  temp_node = (Train_Lattice_node*)(node->get_item_cc());
	  
	  // add node to the lattice
	  //
	  next_node->add_node_cc(temp_node, lmscore, acscore,
				 lnode_a, map_list_a, 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
      //
      next_node = lnode_a[map_list_a[lat_node_a->node_index_d]];
      add_next_node_cc(next_node);
      add_lm_score_cc(lmscore_a);
      add_ac_score_cc(acscore_a);
      next_node->add_prev_node_cc(this);
    }        
  }

  // exit gracefully
  //
  return ISIP_TRUE;
}
