// file: dec_project_7.cc
//
 
// isip include files
//
#include "decoder.h"
#include "decoder_constants.h"
 
// method: set_history_cc
//
// arguments:
//  Trace* trace: (input) the trace to project
//  Lex_node* lexnode: (input) the lex node in this trace
//  int_4 phone: (input) cd model index of the trace instance
//
// return: a logical_1 indicating status
//
// this method determines which words end here and creates history
// nodes for them, then loops over all existing word level traces and
// merges paths if appropriate and otherwise creates a word level
// trace. the merging mechanism is the word-pair approximations. see
// the following references for additional details on word-pair
// approximations
//
// 1.  X. Huang, A. Acero, and H.W. Hon, Spoken Language Processing -
//     A Guide to Theory, Algorithm, and System Development, Prentice
//     Hall, ISBN: 0-13-022616-5, 2001. Section 13.3.3, "Word-Dependent
//     N-best and Word-Lattice Algorithm"
//
// 2. N. Deshmukh, A. Ganapathiraju and J. Picone, "Hierarchical
//    Search for Large Vocabulary Conversational Speech Recognition,"
//    IEEE Signal Processing Magazine, vol. 16, no. 5, pp. 84-107,
//    September 1999. Section 4.3, "Path Merging"
//
// 3.  J. J. Odell, "The Use of Context in Large Vocabulary Speech
//     Recognition," Ph.D. Dissertation, Queens' College, University of
//     Cambridge, March 1995. Section 4.2.3, "N-Best Decoding"
//
logical_1 Decoder::set_history_cc(Trace* trace_a, Lex_node* lexnode_a,
				  int_4 phone_a) {

  // dummy variables
  //
  int_4 ind = (int_4)0;
  float_4 score = (float_4)0;
  Trace* tr = (Trace*)NULL;
  History* trhist = (History*)NULL;
  Link_list* wrd_list = (Link_list*)NULL;
  logical_1 lextype = LXN_WORDGRAPH;
  logical_1 histype = HISTORY_WORDGRAPH;
  float_4 transprob = (float_4)0;
  float_4 scoretemp = (float_4)0;

  // path history of the trace
  //
  History* oldhist = trace_a->get_hist_cc();
  void_p* oldhist_words = (void_p*)NULL;
  oldhist_words = oldhist->get_histwords_cc();

  int_4 nextph = trace_a->get_lex_next_cc()->get_phone_cc();
  Word* curword = (Word*)NULL;
  void_p* newhist_words = (void_p*)NULL;
  
  // list of words that end at this point
  //
  wrd_list = lexnode_a->get_words_cc();
  lextype = lexnode_a->get_type_cc();
  
  // find the nodes corresponding to the words that end here
  //
  for (Link_node* lln = wrd_list->get_head_cc();
       lln != (Link_node*)NULL; lln = lln->get_next_cc()) {
    
    // if this is a lattice-related mode
    //
    if ((function_mode_d == DEC_LATTICE_RESCORE_FUNCTION) ||
	(function_mode_d == DEC_LATTICE_LATTICE_FUNCTION) ||
	(function_mode_d == DEC_LATTICE_VERIFY_FUNCTION)  ||
	(function_mode_d == DEC_FORCE_ALIGN_FUNCTION)) {
   
      // get the current word
      //
      Lattice_node* latnode = (Lattice_node*)(lln->get_item_cc());
      curword = latnode->get_word_cc();

      // get the transitional probability for this current word
      // transitional probability for current word is stored in
      // previous node in the lattice
      // ignore this if lattice verification mode
      //
      if ((function_mode_d != DEC_LATTICE_VERIFY_FUNCTION) &&
	  (function_mode_d != DEC_LATTICE_RESCORE_FUNCTION))
	{
	  Link_list* link_prev = latnode->get_prev_nodes_cc();
	  Link_node* lnkpr = link_prev->get_curr_cc();
	  Lattice_node* lnode = (Lattice_node*)lnkpr->get_item_cc();
	  transprob = lnode->get_lmscore_cc(latnode);
	}
      
      // create the new history node based on the previous histwords and
      // the current node
      // 
      newhist_words = next_histwords_cc(oldhist_words, (void_p)latnode);
      histype = HISTORY_WORDGRAPH;
    }
    
    // otherwise if this is an ngram-related node
    //
    else if ((function_mode_d == DEC_NGRAM_DECODE_FUNCTION) ||
	     (function_mode_d == DEC_LATTICE_GENERATE_FUNCTION)) {
      
      
      // get the unigram node and the word
      //
      Ngram_node* ngnode = (Ngram_node*)(lln->get_item_cc());
      curword = ngnode->get_word_cc();

      // create the new history node based on the previous histwords and
      // the current node
      //
      newhist_words = next_histwords_cc(oldhist_words, (void_p)ngnode);
      
      histype = HISTORY_NGRAM;
    }
    
    // add the word to the list of active words for this frame
    //
    ind = curword->get_index_cc();
    if (word_scores_d[ind] == DEC_DEFAULT_SCORE) {
      active_words_d[num_active_words_d++] = ind;
    }
    
    // update the best word level score
    //
    scoretemp = wdpenalty_d - lmscale_d * transprob;
    score = trace_a->get_score_cc() + scoretemp;
    
    if (score > word_scores_d[ind]) {
      word_scores_d[ind] = score;
    }
    
    // now insert this word in the appropriate word trace list
    // if this word list does not exist create one
    //
    if (word_trlist_d[ind] == (Link_list*)NULL) {
      word_trlist_d[ind] = new Link_list();
    }

    int_4 hist_len = History::get_hist_len_cc();
    logical_1 is_same_trace = ISIP_FALSE;
    
    // loop over all the active traces in the word-level trace list
    //
    for (Link_node* node = word_trlist_d[ind]->get_head_cc();
	 node != (Link_node*)NULL; node = node->get_next_cc()) {
    
      // get the trace in this node and its history
      //
      tr = (Trace*)(node->get_item_cc());
      trhist = tr->get_hist_cc();
      
      // make sure the two traces have the same history and the same
      // next word entry phone
      //
      // the if statement has two test conditions:
      //  test-condition-1: checks if the current modelcvs edit
      //                    (triphone/biphone/monophone) is same
      //  test-condition-2: checks if the monophone in next word is same
      //
      // the idea is to merge the pronunciation variants within the
      // word since we are dealing at word level. this is taken care
      // by the test-condition-2
      //
      // for xwrd models, if the test-condition-1 is true,
      // test-condition-2 is also true. in short, the test-condition-2
      // is sufficient for xwrd models
      //
      // for wint models, the test-condition-1 takes care of the word
      // internal merging. for eg. two instances of x-y and x-y will
      // be merged due to test-condition-1. the test-condition-2 takes
      // care of the merging due to pronunciation variants within
      // words. for eg. two instances x-y and m-n going to the same
      // word entry phone, say a, then both the instances will also be
      // merged
      //
      // for monophone models, same as wint models
      //
      if ((tr->get_inst_cc()->get_phone_ind_cc() == phone_a) ||
	  (tr->get_lex_next_cc()->get_phone_cc() == nextph)) {
	
	is_same_trace = ISIP_TRUE;
	
	// compare each histword
	//
	for (int_4 i = 0; i < hist_len; i++) {
	  if (trhist->get_histwords_cc(i) != newhist_words[i]) {
	    is_same_trace = ISIP_FALSE;
	  }
	}
	
	if (is_same_trace) {
	  // if the new trace is better at word level
	  //
	  if (tr->get_score_cc() < score) {
	    
	    // update the word-level score, phone index and lex node
	    //
	    tr->set_inst_cc(trace_a->get_inst_cc());
	    tr->set_score_cc(score);
	    trhist->set_phone_ind_cc(phone_a);
	  }
	
	  // update the back history
	  //
	  tr->add_path_cc(oldhist, score);

	  // free memory
	  //
	  if (newhist_words != (void_p*)NULL) {
	    delete [] newhist_words;
	  }
	  
	  // exit gracefully
	  //
	  return ISIP_TRUE;
	}
      }
    } // end for loop over all word traces
    
    // if a word-level trace doesn't already exists for this word need
    // to create a new word-level trace --- copy the trace and add the
    // incremental score
    //
    tr = manager_d->new_trace_cc();
    tr->project_trace_cc(trace_a, scoretemp);
    
    // set the correct level
    //
    tr->set_level_cc(DEC_WORD_LEVEL);
    
    // set the trace history
    //
    History* history = manager_d->new_hist_cc();
    history->init_cc(newhist_words, DEC_WORD_LEVEL,
		     trace_a->get_phone_ind_cc(), (int_4)0, frame_d, score);
    history->set_type_cc(histype);
    history_list_d->insert_cc((void_p)history);
    
    // insert history parameters in trace
    //
    tr->set_max_hist_cc(num_hist_d);
    tr->set_hist_cc(history);
    tr->add_path_cc(oldhist, score);
    
    // insert the new trace in the word-level trace list
    //
    insert_word_cc(word_trlist_d[ind], tr);

  } // end for loop over all linknodes
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
