// file: ht_project_2.cc
//

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

// method: generate_phones_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
//  int_4 num_mono : (input) number of monophones
//  int_4 ph_size : (input) phone context size
//  int_4* ph_map : (input) the phone map
//  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
//  mlf_mode : (input) the mlf mode flag
//
// return: a logical_1 indicating status
//
// this method generates the next phones (including crossword phones)
// for the given trace and projects traces onto these
//
logical_1 generate_phones_cc(Train_Trace* trace_a, Train_Link_list** token_list_a,
			     Train_Phone** phones_a, Train_Link_list* treelist_a,
			     Train_Lattice* lattice_a, int_4 num_mono_a,
			     int_4 ph_size_a, int_4* ph_map_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_Lex_node* lexch = (Train_Lex_node*)NULL;
  Train_Hash_cell* hcell = (Train_Hash_cell*)NULL;

  // phone parameters
  //
  int_4* phn = new int_4[ph_size_a];

  // the current trace parameters
  //
  Train_Lex_node* curr_lex = trace_a->get_lex_node_cc();
  Train_Lattice_node* curr_lat = trace_a->get_lat_node_cc();

  // the current phone
  //
  int_4 curr_phn = trace_a->get_phone_ind_cc();
  int_4 mid_phn = phones_a[curr_phn]->get_num_phones_cc() / (int_4)2;
  
  // set the first phone of the triphone
  //
  phn[0] = phones_a[curr_phn]->get_phone_cc(mid_phn);

  // set the middle phone of the triphone if possible
  //
  if (curr_lex != (Train_Lex_node*)NULL) {
    phn[1] = curr_lex->get_phone_cc();
  }
  
  // check if this is end of the current word i.e. start of a new word
  //
  if (trace_a->get_level_cc() == HT_WORD_LEVEL) {
    
    // dummy pointer for start node
    //
    Train_Lex_node* str_lex = (Train_Lex_node*)NULL;
    
    // generate the lexical tree for the end of this word
    //
    lattice_a->get_lat_node_cc(curr_lat, hcell);
    Train_Lex_tree* start_tree = curr_lat->get_lex_tree_cc();
    if (start_tree == (Train_Lex_tree*)NULL) {
      start_tree = new Train_Lex_tree(hcell);
      curr_lat->set_lex_tree_cc(start_tree);
      treelist_a->insert_cc(start_tree);
    }
    
    // make sure this tree is valid (end of sentence will have an
    // invalid tree)
    //
    Train_Lex_node* hdptr = start_tree->get_head_cc();
    if (hdptr == (Train_Lex_node*)NULL) {
      delete [] phn;
      return ISIP_TRUE;
    }

    if(context_mode_a == HT_MONOPHONE) {
      
      // get the list of all possible next phones
      //
      Train_Link_list* start_list = hdptr->get_child_cc();
	
      // loop over all next phones
      //
      for (Train_Link_node* stx = start_list->get_head_cc();
	   stx != (Train_Link_node*)NULL; stx = stx->get_next_cc()) {
	
	// get the child lexical node
	//
	str_lex = (Train_Lex_node*)(stx->get_item_cc());
	
	// set the middle phone of the triphone
	//
	phn[1] = str_lex->get_phone_cc();
	
	// get the list of next phones from the current lexical tree
	//
	Train_Link_list* lex_list = str_lex->get_child_cc();
	for (Train_Link_node* nlx = lex_list->get_head_cc();
	     nlx != (Train_Link_node*)NULL; nlx = nlx->get_next_cc()) {
	  
	  // get the child lexical node
	  //
	  lexch = (Train_Lex_node*)(nlx->get_item_cc());
	  
	  // complete the triphone with the last phone
	  //
	  phn[2] = lexch->get_phone_cc();
	  
	  // grow triphones and project traces
	  //
	  grow_phone_cc(trace_a, token_list_a, phones_a,
			treelist_a, lattice_a, curr_lat, str_lex,
			lexch, ph_size_a, num_mono_a,
			ph_map_a, phn, wd_active_a, wd_numact_a, ph_active_a,
			ph_numact_a, ngen_a, max_score_a, context_mode_a,
			mlf_mode_a, num_sph_a, sph_index_a);
	  
	} // end loop over all phones
      } // end loop over all start phones
    }
    
    else{
      
      // check if the next phone is already defined --- this will be the
      // case if the current word does not end in sil
      //
      if (mid_phn > (int_4)0) {
	
	if (context_mode_a == HT_WRD_INTERNAL) {
	  
	  int_4 num_phone = phones_a[curr_phn]->get_num_phones_cc();
	  if (num_phone == 2) {
	    phn[0] = 0;
	    
	    Train_Link_list* lex_list1 = hdptr->get_child_cc();
	    for (Train_Link_node* nlx1 = lex_list1->get_head_cc();
		 nlx1 != (Train_Link_node*)NULL; nlx1 = nlx1->get_next_cc()) {
	      
	      // get the child lexical node
	      //
	      str_lex = (Train_Lex_node*)(nlx1->get_item_cc());
	      phn[1] = str_lex->get_phone_cc();
	      
	      Train_Link_list* lex_list2 = str_lex->get_child_cc();
	      for (Train_Link_node* nlx2 = lex_list2->get_head_cc();
		   nlx2 != (Train_Link_node*)NULL; nlx2 = nlx2->get_next_cc()) {
		
		// get the child node
		//
		lexch = (Train_Lex_node*)(nlx2->get_item_cc());
		phn[2] = lexch->get_phone_cc();
		
		// grow phone
		//
		grow_phone_cc(trace_a, token_list_a, phones_a, treelist_a,
			      lattice_a, curr_lat, str_lex, lexch, ph_size_a,
			      num_mono_a, ph_map_a, phn, wd_active_a,
			      wd_numact_a, ph_active_a, ph_numact_a, ngen_a,
			      max_score_a, context_mode_a, mlf_mode_a,
			      num_sph_a, sph_index_a);
	      } // end loop all the phn[2]
	    } // end loop all the phn[1]
	  } // end loop if this is a diphone
	} // end if context_mode equals word-internal mode
	
	else if (context_mode_a == HT_DEF_TRIPHONE) {
	  
	  // set the middle phone of the triphone
	  //
	  phn[1] = phones_a[curr_phn]->get_phone_cc(mid_phn + (int_4)1);
	  
	  // find the lexical node in the tree that contains this monophone
	  //
	  str_lex = hdptr->get_node_cc(phn[1]);
	  
	  // get the list of next phones from the current lexical tree
	  //
	  Train_Link_list* lex_list = str_lex->get_child_cc();
	  for (Train_Link_node* nlx = lex_list->get_head_cc();
	       nlx != (Train_Link_node*)NULL; nlx = nlx->get_next_cc()) {
	    
	    // get the child lexical node
	    //
	    lexch = (Train_Lex_node*)(nlx->get_item_cc());
	    
	    // complete the triphone with the last phone
	    //
	    phn[2] = lexch->get_phone_cc();
	    
	    // grow triphones and project traces
	    //
	    grow_phone_cc(trace_a, token_list_a, phones_a, treelist_a,
			  lattice_a, curr_lat, str_lex, lexch, ph_size_a,
			  num_mono_a, ph_map_a, phn, wd_active_a,
			  wd_numact_a, ph_active_a, ph_numact_a,
			  ngen_a, max_score_a, context_mode_a,
			  mlf_mode_a, num_sph_a, sph_index_a);	  
	  } // end loop over all phones
	} // end if mode equals triphone system
      } // end if next phone is already known
      
      // otherwise the word ended in sil and we need to extend all
      // possible next phones
      //
      else {
	
	// get the list of all possible next phones
	//
	Train_Link_list* start_list = hdptr->get_child_cc();
	
	// loop over all next phones
	//
	for (Train_Link_node* stx = start_list->get_head_cc();
	     stx != (Train_Link_node*)NULL; stx = stx->get_next_cc()) {
	  
	  // get the child lexical node
	  //
	  str_lex = (Train_Lex_node*)(stx->get_item_cc());
	  
	  // set the middle phone of the triphone
	  //
	  phn[1] = str_lex->get_phone_cc();
	  
	  // get the list of next phones from the current lexical tree
	  //
	  Train_Link_list* lex_list = str_lex->get_child_cc();
	  for (Train_Link_node* nlx = lex_list->get_head_cc();
	       nlx != (Train_Link_node*)NULL; nlx = nlx->get_next_cc()) {
	    
	    // get the child lexical node
	    //
	    lexch = (Train_Lex_node*)(nlx->get_item_cc());
	    
	    // complete the triphone with the last phone
	    //
	    phn[2] = lexch->get_phone_cc();
	    
	    if (context_mode_a == HT_WRD_INTERNAL) {
	      
	      // change the phn[0]
	      //
	      phn[0] = 0;
	      
	      // grow triphones and project traces
	      //
	      grow_phone_cc(trace_a, token_list_a, phones_a, treelist_a,
			    lattice_a, curr_lat, str_lex, lexch,
			    ph_size_a, num_mono_a, ph_map_a, phn,
			    wd_active_a, wd_numact_a, ph_active_a,
			    ph_numact_a, ngen_a, max_score_a, context_mode_a,
			    mlf_mode_a, num_sph_a, sph_index_a);
	    }

	    else if (context_mode_a == HT_DEF_TRIPHONE) {
	      
	      // grow triphones and project traces
	      //
	      grow_phone_cc(trace_a, token_list_a, phones_a, treelist_a,
			    lattice_a, curr_lat, str_lex, lexch,
			    ph_size_a, num_mono_a, ph_map_a, phn,
			    wd_active_a, wd_numact_a, ph_active_a,
			    ph_numact_a, ngen_a, max_score_a, context_mode_a,
			    mlf_mode_a, num_sph_a, sph_index_a);
	      
	    }
	  } // end loop over all phones
	} // end loop over all start phones
      } // end else need to loop over all start phones
    }
  } // end if start of new word
  
  // otherwise this is a word-internal case
  //
  else if ((curr_lex != (Train_Lex_node*)NULL) && (phn[1] != TRAIN_LXN_STOP_PHONE)) {
    
    // get the list of next phones from the current lexical tree
    //
    Train_Link_list* lex_list = curr_lex->get_child_cc();
    for (Train_Link_node* nlx = lex_list->get_head_cc(); nlx != (Train_Link_node*)NULL;
	 nlx = nlx->get_next_cc()) {
      
      // get the child lexical node
      //
      lexch = (Train_Lex_node*)(nlx->get_item_cc());
      
      // complete the triphone with the last phone
      //
      phn[2] = lexch->get_phone_cc();
      
      // grow triphones and project traces
      //
      grow_phone_cc(trace_a, token_list_a, phones_a, treelist_a, lattice_a,
		    curr_lat, curr_lex, lexch, ph_size_a, num_mono_a,
		    ph_map_a, phn, wd_active_a, wd_numact_a, ph_active_a,
		    ph_numact_a, ngen_a, max_score_a, context_mode_a,
		    mlf_mode_a, num_sph_a, sph_index_a);
	  
    } // end loop over all phones
  } // end else not start of sentence
  
  // free memory
  //
  delete [] phn;
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
