// file: ht_lattice_1.cc
//

// isip include files
//
#include "hmm_train.h"
#include "hmm_train_constants.h"

// method: construct_lattice_cc 
//
// arguments:
//
//  Train_Trace* trace: (input) trace to add to the lattice
//  Train_Lattice_node* latnode: (input) the current lattice node
//  float_8 score: (input) path score till this point
//  float_4 lmscale: (input) the desired language model scale factor
//  float_4 wdpenalty: (input) the desired word insertion penalty
//  int_4& num_nodes: (output) number of nodes in the lattice
//  int_4& num_arcs: (output) number of arcs in the lattice
//  Train_Hash_table*& lathash: (output) a hash table containing the lattice nodes
//
// return: a logical_1 indicating status
//
// this recursive method checks if the current trace has already been
// covered by the lattice building process; and if it isn't, adds it
// to the lattice node table and then loops over all its back-pointers
//
logical_1 construct_lattice_cc(Train_Trace* trace_a, Train_Lattice_node* latnode_a,
			       float_8 score_a, float_4 lmscale_a,
			       float_4 wdpenalty_a, int_4& num_nodes_a,
			       int_4& num_arcs_a, Train_Hash_table*& lathash_a) {

  // dummy variables
  //
  Train_Hash_cell* hcell = (Train_Hash_cell*)NULL;
  Train_Lattice_node* latn = (Train_Lattice_node*)NULL;

  // get the back-pointer information
  //
  Train_Trace** back = trace_a->get_back_ptr_cc();
  int_4 num = trace_a->get_back_size_cc();

  // if this is not a word-level trace go to its back-pointer
  //
  if ((trace_a->get_level_cc() != HT_WORD_LEVEL) && (back != (Train_Trace**)NULL)) {

    // loop over all back-pointers
    //
    for (int_4 i = 0; (i < num) && (back[i] != (Train_Trace*)NULL); i++) {
      construct_lattice_cc(back[i], latnode_a, score_a, lmscale_a,
			   wdpenalty_a, num_nodes_a, num_arcs_a, lathash_a);
    }
    return ISIP_TRUE;
  }
  
  // get the scores in this trace
  //
  float_8 score = trace_a->get_score_cc();
  float_8 lmscore = wdpenalty_a;
  if (back != (Train_Trace**)NULL) {
    lmscore = trace_a->get_lex_node_cc()->get_max_score_cc();
  }
  float_8 acscore = score_a - score - lmscore;
  lmscore -= wdpenalty_a;
  
  // create a string to hash on for this trace
  //
  char_1* str = new char_1[TRAIN_LATTICE_KEYSTR_LENGTH];
  sprintf((char*)str, "%p", trace_a);
  if (back == (Train_Trace**)NULL) {
    sprintf((char*)str, "%p", (Train_Trace*)NULL);
  }
  
  // if this trace is already covered by the lattice, only need to set
  // up the arc and return
  //
  hcell = lathash_a->hash_lookup_cc(str);
  if ((hcell != (Train_Hash_cell*)NULL) && (latnode_a != (Train_Lattice_node*)NULL)) {

    // get the lattice node here
    //
    latn = (Train_Lattice_node*)(hcell->get_item_cc());
    
    // 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);
  
    // exit gracefully
    //
    return ISIP_TRUE;
  }  

  // memory manager
  //
  Train_Memory_manager* manager = Train_Link_list::get_manager_cc();

  // create a lattice node for this trace
  //
  latn = manager->new_lat_cc();
  latn->set_node_index_cc(num_nodes_a++);
  latn->set_frame_index_cc(trace_a->get_frame_ind_cc());

  // set the word information
  //
  Train_Lattice_node* trln = trace_a->get_lat_node_cc();
  Train_Word* word = trln->get_word_cc();
  latn->set_word_cc(word);

  // set up the arc connections
  //
  if (latnode_a != (Train_Lattice_node*)NULL) {
    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);
  }
  
  // add this lattice node to the lattice node hash table
  //
  hcell = manager->new_hash_cc();
  hcell->set_cc(str, latn);
  lathash_a->hash_insert_cc(hcell);
  
  // loop over all the back-pointers
  //
  if (back != (Train_Trace**)NULL) {
    for (int_4 i = 0; (i < num) && (back[i] != (Train_Trace*)NULL); i++) {
      
      // for each valid trace back-track over all paths merging here
      // and construct lattice
      //      
      construct_lattice_cc(back[i], latn, score, lmscale_a, wdpenalty_a,
			   num_nodes_a, num_arcs_a, lathash_a);
    }
  }

  // free memory
  //
  delete [] str;

  // exit gracefully
  //
  return ISIP_TRUE;
}
