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

// isip include files
//
#include "decoder.h"
#include "decoder_constants.h"

// method: ngram_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
//  void_p oldnode_a: (input) the previous history 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::ngram_to_lattice_cc(int_4& num_nodes_a,
				       int_4& num_arcs_a,
				       Hash_table*& newlat_a,
				       History* hist_a,
				       Lattice_node* latnode_a,
				       void_p oldnode_a,
				       float_4 score_a) {
  // dummy variables
  //
  Hash_cell* hcell = (Hash_cell*)NULL;
  Ngram_node* ngnode = (Ngram_node*)NULL;
  Lattice_node* olat = (Lattice_node*)NULL;
  Lattice_node* latn = (Lattice_node*)NULL;
  void_p node = (void_p)NULL;
  static char_1 str[LATTICE_KEYSTR_LENGTH];
  static Ngram_node* lmnode = (Ngram_node*)NULL;
  
  Word* histwd = (Word*)NULL;
  Word* curwd = (Word*)NULL;
  Word** words = (Word**)NULL;

  int_4 level = (int_4)0;
  int_4 wdcount = (int_4)0;
  
  float_4 trscore = (float_4)0;
  float_4 acscore = (float_4)0;
  float_4 lmscore = (float_4)0;

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

    // get the path score of this trace
    //
    trscore = hist_a->get_score_cc();
    
    // get the history word
    //
    node = hist_a->get_histwords_cc((int_4)0);
    logical_1 htype = hist_a->get_type_cc();

    // first handle LM switching boundaries, if any
    //    
    // if the history node is not null
    //
    if (node != (void_p)NULL) {

      // the list of previous history words
      //
      histlist = hist_a->get_prev_list_cc();
      
      // if the history type is ngram node
      //
      if (htype == HISTORY_NGRAM) {

	// get the word
	//
	ngnode = (Ngram_node*)node;
	histwd = ngnode->get_word_cc();

	// if this is an LM switch and if this is not the origin of
	// all traces
	//
	if ((histwd != (Word*)NULL) &&
	    (*(histwd->get_name_cc()) == ISIP_DOLLAR) &&
	    (histlist != (Link_list*)NULL)) {
	  
	  // save the history node
	  //
	  lmnode = ngnode;
	  
	  // 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
	    //
	    ngram_to_lattice_cc(num_nodes_a, num_arcs_a, newlat_a, histnode,
				latn, node, scorelist[idx]);
	    
	  } // end loop for all back pointers

	  // exit gracefully
	  //
	  return ISIP_TRUE;

	} // end if this is an LM switch node
      } // end if this is ngram type history node
      
      // otherwise the history node is a lattice node
      //
      else {

	// get the word
	//
	olat = (Lattice_node*)node;
	histwd = olat->get_word_cc();

	// if this is a LM switching point
	//
	if (histwd == (Word*)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
	    //
	    ngram_to_lattice_cc(num_nodes_a, num_arcs_a, newlat_a, histnode,
				latnode_a, lmnode, score_a);

	  } // end loop for all back pointers
	  
	  // exit gracefully
	  //
	  return ISIP_TRUE;
	  
	} // end if this is LM switching mode
      } // end else
    } // end if node is not null

    // list of all previous words
    //
    histlist = hist_a->get_prev_list_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 trace is not already covered by the lattice
    //
    if (hcell == (Hash_cell*)NULL) {

      // create new lattice node for this trace
      //
      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(histwd);

      // 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) {

      // connect this lattice node
      //
      latnode_a->add_prev_node_cc(latn);
      
      // find the LM score with this history
      //
      if (htype == HISTORY_NGRAM) {
	
	// get the current word
	//
	curwd = (Word*)NULL;
	if (lmnode != (Ngram_node*)NULL) {
	  curwd = lmnode->get_word_cc();
	}
	else {
	  curwd = latnode_a->get_word_cc();
	}
	
	// create a list of words for ngram sequence
	//
	words = new Word*[ngram_order_d];
	wdcount = (int_4)0;
	level = (int_4)0;
	
	// get the current word and put it in the word list
	//
	words[ngram_order_d - 1] = curwd;
	wdcount++;
	level++;
	if (histwd != (Word*)NULL) {
	  words[ngram_order_d - 1 - wdcount] = histwd;
	  wdcount++;
	  level++;
	}
	
	// add corresponding arcs to the lattice
	//
	add_arcs_cc (level, words, wdcount, score_a - trscore,
		     hist_a, latnode_a, latn, num_arcs_a);

	delete [] words;

      }
      
      // otherwise gneed to get scores from previous lattice node
      //
      else {
	
	// get the scores
	//
	lmscore = olat->get_lmscore_cc((Lattice_node*)oldnode_a);
	acscore = score_a - trscore - (lmscore * lmscale_d + wdpenalty_d);
	
	// set up the arc connections
	//
	latn->add_next_node_cc(latnode_a);
	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 lattice 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 traces
    //
    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
	//
	ngram_to_lattice_cc(num_nodes_a, num_arcs_a, newlat_a, histnode,
			    latn, node, scorelist[idx]);
      }
    } // end if backpointer not null
  } // end if word level trace
  
  // otherwise keep going till find the next word-level trace
  //
  else {
    
    // if this is not the origin of all traces
    //
    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
	//
	ngram_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 trace

  // exit gracefully
  //
  return ISIP_TRUE;
}
