// file: dec_lat_4.cc
//
 
// system include files
//
#include <string.h>

// isip include files
//
#include "decoder.h"
#include "decoder_constants.h"
 
// method: lattice_to_lattice_cc
//
// arguments:
//  int_4& num_nodes: (input/output) number of nodes
//  int_4& num_arcs: (input/output) number of arcs
//  Hash_table*& newlat: (input/output) hash table of new lat nodes
//  History* hist: (input) history node to be added to lattice
//  Lattice_node* latnode: (input) current new lattice node
//  Lattice_node* oldnode: (input) current old lattice node
//  float_4 score: (input) path score at this point
//
// return: a logical_1 indicating status
//
// this method recursively backtracks over history nodes on a path and
// checks if it is already covered by the new lattice, if not it adds
// a corresponding lattice node
//
logical_1 Decoder::lattice_to_lattice_cc(int_4& num_nodes_a,
				       int_4& num_arcs_a,
				       Hash_table*& newlat_a,
				       History* hist_a,
				       Lattice_node* latnode_a,
				       Lattice_node* oldnode_a,
				       float_4 score_a) {

  // dummy variables
  //
  Hash_cell* hcell = (Hash_cell*)NULL;
  Lattice_node* olat = (Lattice_node*)NULL;
  Lattice_node* latn = (Lattice_node*)NULL;
  Word* word = (Word*)NULL;
  static char_1 str[LATTICE_KEYSTR_LENGTH];
  
  float_4 trscore = (float_4)0;
  float_4 acscore = (float_4)0;
  float_4 lmscore = (float_4)0;

  logical_1 exist_flag = ISIP_FALSE;
  logical_1 link_flag = ISIP_FALSE;
  
  // get the history information for this history node
  //
  Link_list* histlist = hist_a->get_prev_list_cc();
  History* histnode = (History*)NULL;
  
  // if this is a word-level history node
  //
  if (hist_a->get_level_cc() == DEC_WORD_LEVEL) {

    // get the path score of this history node
    //
    trscore = hist_a->get_score_cc();
    
    // get the history word list
    //
    void_p node = hist_a->get_histwords_cc((int_4)0);
    if (node != (void_p)NULL) {
      olat = (Lattice_node*)node;
      word = olat->get_word_cc();
    }
    
    // set the key string
    //
    strcpy((char*)str, "");
    sprintf((char*)str, "%p", (History*)NULL);
    if (histlist != (Link_list*)NULL) {
      sprintf((char*)str, "%p", hist_a);
    }
    
    // get the lattice hash cell at this location
    //
    hcell = newlat_a->hash_lookup_cc(str);
    
    // if this history node is not already covered by the lattice
    //
    if (hcell == (Hash_cell*)NULL) {
      
      // create new lattice node for this history node
      //
      latn = manager_d->new_lat_cc();
      latn->set_node_index_cc(num_nodes_a);
      latn->set_frame_index_cc(hist_a->get_frame_ind_cc());
      num_nodes_a++;
      
      // set the word information
      //
      latn->set_word_cc(word);
      
      // add this lattice node to the lattice node hash table
      //
      Hash_cell* latcell = manager_d->new_hash_cc();
      latcell->set_cc(str, latn);
      newlat_a->hash_insert_cc(latcell);
    }
    
    // otherwise the lattice node already exists
    //
    else {
      
      // get the lattice node here
      //
      latn = (Lattice_node*)(hcell->get_item_cc());
      
      // set the exist flag
      //
      exist_flag = ISIP_TRUE;
    }
    
    // if the current input lattice node is not the end of the lattice
    //
    if (latnode_a != (Lattice_node*)NULL) {

      // make sure this link is not already present
      //
      Link_list* latlist = latn->get_next_nodes_cc();
      if (latlist != (Link_list*)NULL) {
	for (Link_node* lnd = latlist->get_head_cc();
	     lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	  if (latnode_a == (Lattice_node*)(lnd->get_item_cc())) {
	    link_flag = ISIP_TRUE;
	    break;
	  }
	}
      }
      
      // add this link only if necessary
      //
      if (link_flag == ISIP_FALSE) {

	// get the lm score for this arc
	//
	lmscore = olat->get_lmscore_cc(oldnode_a);
	
	// get the acoustic score
	//
	acscore = score_a - trscore - (lmscore * lmscale_d + wdpenalty_d);
	
	// set up the arc connections
	//
	latn->add_next_node_cc(latnode_a);
	latnode_a->add_prev_node_cc(latn);
	num_arcs_a++;
	
	// add the language model and acoustic scores
	//
	latn->add_lm_score_cc(lmscore);
	latn->add_ac_score_cc(acscore);
      }
    } // end if input node is not null
    
    // if no need to extend this path, return
    //
    if (exist_flag == ISIP_TRUE) {
      return ISIP_TRUE;
    }
    
    // if this is not the origin of all history nodes
    //
    if (histlist != (Link_list*)NULL) {

      // get the acoustic path scores
      //
      int_4 idx = (int_4)0;
      float_4* scorelist = hist_a->get_prev_score_cc();
      
      // loop over all back-pointers
      //
      for (Link_node* lnd = histlist->get_head_cc();
	   lnd != (Link_node*)NULL; lnd = lnd->get_next_cc(), idx++) {
	
	// get the history node here
	//
	histnode = (History*)(lnd->get_item_cc());
	
	// traverse the path back
	//
	lattice_to_lattice_cc(num_nodes_a, num_arcs_a, newlat_a, histnode,
			      latn, olat, scorelist[idx]);
      }
    } // end if backpointer not null
  } // end if word level history node
  
  // otherwise keep going till find the next word-level history node
  //
  else {
    
    // if this is not the origin of all history nodes
    //
    if (histlist != (Link_list*)NULL) {

      // loop over all back-pointers
      //
      for (Link_node* lnd = histlist->get_head_cc();
	   lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	
	// get the history node here
	//
	histnode = (History*)(lnd->get_item_cc());
	
	// traverse the path back
	//
	lattice_to_lattice_cc(num_nodes_a, num_arcs_a, newlat_a, histnode,
			      latnode_a, oldnode_a, score_a);
      }
    } // end if backpointer not null
  } // end else not word-level history node
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
