// file: ht_project_3.cc
//

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

// method: grow_phone_cc
//
// arguments:
//  Train_Trace* trace : (input) the input trace to be projected
//  Train_Link_list** token_list : (input) linked lists of active tokens
//  Train_Phone** phones : (input) phone models
//  Train_Link_list* treelist : (input) list of active lexical trees
//  Train_Lattice* lattice: (input) the lattice for this application
//  Train_Lattice_node* curr_lat : (input) the current lattice node
//  Train_Lex_node* currlex : (input) the lexical tree node for the middle phone
//  Train_Lex_node* lexch : (input) the lexical tree node for the new trace
//  int_4 ph_size : (input) phone context size
//  int_4 num_mono : (input) number of monophones
//  int_4* ph_map : (input) the phone map
//  int_4*& phn : (input) the three phones constituting this triphone
//  int_4*& wd_active : (output) list of active words
//  int_4& wd_numact : (output) number of active words
//  int_4*& ph_active : (output) list of active phones
//  int_4& ph_numact : (output) number of active phones
//  int_4*& ngen : (output) number of traces generated
//  float_8& max_score : (output) maximum path score so far
//  logical_1 mlf_mode : (input) the mlf mode flag
//
// return: a logical_1 indicating status
//
// this method checks the phones that creates the next triphone for
// the given trace and projects traces onto these
//
logical_1 grow_phone_cc(Train_Trace* trace_a, Train_Link_list** token_list_a,
			Train_Phone** phones_a, Train_Link_list* treelist_a,
			Train_Lattice* lattice_a, Train_Lattice_node* curr_lat_a,
			Train_Lex_node* currlex_a, Train_Lex_node* lexch_a,
			int_4 ph_size_a, int_4 num_mono_a, int_4* ph_map_a,
			int_4*& phn_a, int_4*& wd_active_a,
			int_4& wd_numact_a, int_4*& ph_active_a,
			int_4& ph_numact_a, int_4*& ngen_a,
			float_8& max_score_a, int_4 context_mode_a,
			logical_1 mlf_mode_a, int_4 num_sph_a,
			int_4* sph_index_a) {

  // dummy variables
  //
  Train_Lattice_node* latnode = (Train_Lattice_node*)NULL;
  Train_Lex_node* lexnode = (Train_Lex_node*)NULL;
  Train_Hash_cell* hcell = (Train_Hash_cell*)NULL;
  
  // word transition parameters
  //
  int_4 num_wd = (int_4)0;
  float_8* lmscore = (float_8*)NULL;
  
  // the triphone index
  //
  int_4 phn_ind = (int_4)-1;
  
  // if we are approaching an end of word and need to grow cross-word
  // triphones
  //
  if (phn_a[2] == TRAIN_LXN_STOP_PHONE) {
    
    // if the word ends in sp then the current lattice node is known
    //
    if (phn_a[1] == HT_SP_PHONE) {

      // set the correct lattice node that ends this word
      //
      latnode = curr_lat_a;
      
      // get the phone index
      //
      phn_ind = ph_map_a[get_nphone_ind_cc(ph_size_a, num_mono_a, phn_a,
                            context_mode_a, num_sph_a, sph_index_a)];

      if (mlf_mode_a == HT_MODEL_TRANS) {
	phn_ind = currlex_a->get_phone_ind_cc();
      }
              
      // grow new traces for this phone
      //
      grow_trace_cc(trace_a, token_list_a[phn_ind], lexch_a, latnode,
		    (float_8)0, phones_a, phn_ind, wd_active_a, wd_numact_a,
		    ph_active_a, ph_numact_a, ngen_a, max_score_a);
    }

    // otherwise need to find out which word ended at this point
    //
    else {
      
      // list of words that end at this point
      //
      int_4 ww = (int_4)0;

      Train_Link_list* lat_list = lexch_a->get_words_cc();
      lexch_a->get_score_list_cc(num_wd, lmscore);
      
      // find the lattice nodes corresponding to the words that end
      // here
      //
      for (Train_Link_node* ltn = lat_list->get_head_cc();
	   ltn != (Train_Link_node*)NULL; ltn = ltn->get_next_cc()) {

	// get the lattice node
	//
	latnode = (Train_Lattice_node*)(ltn->get_item_cc());

	// if this is going to be a silence phone, no need to build
	// the right context
	//
	if (phn_a[1] == HT_SILENCE_PHONE || check_sph_cc(phn_a[1], num_sph_a,
							 sph_index_a)) {
	  // get the phone index
	  //
	  phn_ind = ph_map_a[get_nphone_ind_cc(ph_size_a, num_mono_a, phn_a,
                                               context_mode_a, num_sph_a,
					       sph_index_a)];
	  	  
	  // grow new traces for this phone
	  //
	  grow_trace_cc(trace_a, token_list_a[phn_ind], lexch_a, latnode,
			lmscore[ww], phones_a, phn_ind, wd_active_a,
			wd_numact_a, ph_active_a, ph_numact_a, ngen_a,
			max_score_a);
	  
	} // end if silence phone
    
	// otherwise grow triphones into next words
	//
	else {
	  
	  if (context_mode_a == HT_WRD_INTERNAL) {
	    
	    // build the lexical tree for the end of the current word
	    //
	    lattice_a->get_lat_node_cc(latnode, hcell);
	    Train_Lex_tree* lex_tree = latnode->get_lex_tree_cc();
	    if (lex_tree == (Train_Lex_tree*)NULL) {
	      lex_tree = new Train_Lex_tree(hcell);
	      latnode->set_lex_tree_cc(lex_tree);
	      treelist_a->insert_cc(lex_tree);
	    }
	    
	    // make sure the next phone is sp
	    //
	    Train_Lex_node* lexnext = currlex_a->get_node_cc(HT_SP_PHONE);
	    
	    // complete the triphone with the last phone
	    //
	    phn_a[2] = 0;
	    
	    // get the phone index
	    //
	    phn_ind = ph_map_a[get_nphone_ind_cc(ph_size_a, num_mono_a,
						 phn_a, context_mode_a,
						 num_sph_a, sph_index_a)];

	    if (mlf_mode_a == HT_MODEL_TRANS) {
	      phn_ind = currlex_a->get_phone_ind_cc();
	    }
	    
	    if (phones_a[phn_ind] != (Train_Phone*)NULL) {
	      
	      // grow new traces for this phone
	      //
	      grow_trace_cc(trace_a, token_list_a[phn_ind], lexnext, latnode,
			    lmscore[ww], phones_a, phn_ind, wd_active_a,
			    wd_numact_a, ph_active_a, ph_numact_a, ngen_a,
			    max_score_a);
	    }
	  }
	  else if ((context_mode_a == HT_DEF_TRIPHONE) ||
		   (context_mode_a == HT_MONOPHONE)) {
	    
	    
	    // build the lexical tree for the end of the current word
	    //
	    lattice_a->get_lat_node_cc(latnode, hcell);
	    Train_Lex_tree* lex_tree = latnode->get_lex_tree_cc();
	    if (lex_tree == (Train_Lex_tree*)NULL) {
	      lex_tree = new Train_Lex_tree(hcell);
	      latnode->set_lex_tree_cc(lex_tree);
	      treelist_a->insert_cc(lex_tree);
	    }
	    
	    // get the head of the next lexical tree
	    //
	    Train_Lex_node* head = lex_tree->get_head_cc();
	    
	    // get the list of all possible next phones
	    //
	    Train_Link_list* nlx_list = head->get_child_cc();
	    
	    // make sure the next phone is sp
	    //
	    Train_Lex_node* lexnext = currlex_a->get_node_cc(HT_SP_PHONE);
	    
	    // loop over all next phones and create traces
	    //
	    for (Train_Link_node* lxn = nlx_list->get_head_cc();
		 lxn != (Train_Link_node*)NULL; lxn = lxn->get_next_cc()) {
	      
	      // get the child lexical node
	      //
	      lexnode = (Train_Lex_node*)(lxn->get_item_cc());
	      
	      // complete the triphone with the last phone
	      //
	      phn_a[2] = lexnode->get_phone_cc();
	      
	      // get the phone index
	      //
	      phn_ind = ph_map_a[get_nphone_ind_cc(ph_size_a, num_mono_a,
						   phn_a, context_mode_a,
						   num_sph_a, sph_index_a)];
	      if (mlf_mode_a == HT_MODEL_TRANS) {
		phn_ind = currlex_a->get_phone_ind_cc();
	      }
	      // grow new traces for this phone
	      //
	      grow_trace_cc(trace_a, token_list_a[phn_ind], lexnext, latnode,
			    lmscore[ww], phones_a, phn_ind, wd_active_a,
			    wd_numact_a, ph_active_a, ph_numact_a, ngen_a,
			    max_score_a);
	      if(context_mode_a==HT_MONOPHONE){
		break;
	      }
	      
	    } // end loop over all next phones
	  }	
	} // end else grow triphones

	// increment word count
	//
	ww++;
	
      } // end loop over all lattice nodes
    } // else not sp phone
  } // end else this is a word end
  
  // otherwise this is not an end of word
  //
  else if (phn_a[2] != HT_SP_PHONE || mlf_mode_a == HT_MODEL_TRANS) {
    
    // get the phone index
    //
    phn_ind = ph_map_a[get_nphone_ind_cc(ph_size_a, num_mono_a, phn_a, 
                                         context_mode_a, num_sph_a,
					 sph_index_a)];
    if (mlf_mode_a == HT_MODEL_TRANS) {
      phn_ind = currlex_a->get_phone_ind_cc();
    }
    // grow new traces for this phone
    //
    grow_trace_cc(trace_a, token_list_a[phn_ind], lexch_a, curr_lat_a,
		  lexch_a->get_max_score_cc(), phones_a, phn_ind, wd_active_a,
		  wd_numact_a, ph_active_a, ph_numact_a, ngen_a,
		  max_score_a);
    
  } // end if not end of word
  
  // exit gracefully
  //
  return ISIP_TRUE;
}




