// file: dec_project_3.cc
//
 
// isip include files
//
#include "decoder.h"
#include "decoder_constants.h"
 
// method: next_models_cc
//
// arguments:
//  Trace* trace: (input) the trace to project to next model
//  
// return: a logical_1 indicating status
//
// this method generates the next model index and projects traces to
// the entry state of the next model, for model end traces as well as
// word boundaries
//
logical_1 Decoder::next_models_cc(Trace* trace_a) {

  // dummy variables
  //
  Lex_tree* start_tree = (Lex_tree*)NULL;
  Lex_node* hdptr = (Lex_node*)NULL;
  Lex_node* lexch = (Lex_node*)NULL;
  Lex_node* str_lex = (Lex_node*)NULL;
  Word* word = (Word*)NULL;
  logical_1 flag = ISIP_FALSE;
  
  // phone parameters
  //
  int_4* phn = new int_4[cd_size_d];
  for (int_4 i = 0; i < cd_size_d; i++) {
    phn[i] = LXN_NULL_PHONE;
  }
  int_4 phtmp = LXN_NULL_PHONE;
  
  // the current instance
  //
  Instance* inst = trace_a->get_inst_cc();
  
  // the current lexical node
  //
  Lex_node* curr_lex = inst->get_lex_node_cc();
  logical_1 lextype = curr_lex->get_type_cc();
  
  // history-related parameters
  //
  History* hist = trace_a->get_hist_cc();
  void_p hist_word = hist->get_histwords_cc((int_4)0);

  // the current phone
  //
  int_4 curr_phn = inst->get_phone_ind_cc();
  int_4 mid_phn = cd_models_d[curr_phn]->get_num_phones_cc() / (int_4)2;
  
  // set the first phone of the triphone
  //
  if (context_mode_d == DEC_CROSS_WORD_MODE) {
    phn[0] = cd_models_d[curr_phn]->get_phone_cc(mid_phn);
  }

  // if this is lattice mode get the lattice node and the lexical tree
  //
  if ((function_mode_d == DEC_LATTICE_RESCORE_FUNCTION) ||
      (function_mode_d == DEC_LATTICE_VERIFY_FUNCTION) ||
      (function_mode_d == DEC_LATTICE_LATTICE_FUNCTION) ||
      (function_mode_d == DEC_FORCE_ALIGN_FUNCTION)) {
    
    // get the current word
    //
    Lattice_node* curr_lat = (Lattice_node*)hist_word;
    word = curr_lat->get_word_cc();
    
    // get the tree and the start lex node
    //
    start_tree = curr_lat->get_lex_tree_cc();
    if (start_tree == (Lex_tree*)NULL) {	  
      start_tree = new Lex_tree(curr_lat);
      curr_lat->set_lex_tree_cc(start_tree);
      lextree_list_d->insert_cc(start_tree);
    }
    hdptr = start_tree->get_head_cc();
  }
  
  // otherwise this is ngram mode, set the lexical tree
  //
  else if ((function_mode_d == DEC_NGRAM_DECODE_FUNCTION) ||
	   (function_mode_d == DEC_LATTICE_GENERATE_FUNCTION)) {

    // if this is a dynamic network LM
    //
    if (lextype == LXN_WORDGRAPH) {
      
      // get the current word
      //
      Lattice_node* curr_lat = (Lattice_node*)hist_word;
      word = curr_lat->get_word_cc();
      
      // get the tree and the start lex node
      //
      start_tree = curr_lat->get_lex_tree_cc();
      if (start_tree == (Lex_tree*)NULL) {	  
	start_tree = new Lex_tree(curr_lat);
	curr_lat->set_lex_tree_cc(start_tree);
	lextree_list_d->insert_cc(start_tree);
      }
      hdptr = start_tree->get_head_cc();
    }

    // otherwise it is an ngram node in the lex node
    //
    else if (lextype == LXN_NGRAM) {
      
      // if there is a history word, then the full tree needs to be used
      //
      if (hist_word != (void_p)NULL) {
	
	// set the tree and the start lex node
	//
	start_tree = proto_tree_d;
	hdptr = start_tree->get_head_cc();
	
	// if this is a sentence end word no need to grow further
	//
	Ngram_node* ngnode = (Ngram_node*)hist_word;
	word = ngnode->get_word_cc();
	if (word->get_index_cc() == DEC_SENT_END_WORD) {
	  hdptr = (Lex_node*)NULL;
	}
      }

      // otherwise use the sentence start tree
      //
      else {
	start_tree = (Lex_tree*)(lextree_list_d->get_head_cc()->get_item_cc());
	hdptr = start_tree->get_head_cc();
      }
    } // end else if ngram word node
  } // end if ngram decoding modes
  
  // check if this is end of the current word i.e. start of a new word
  //
  if (trace_a->get_level_cc() == DEC_WORD_LEVEL) {
    
    // make sure this tree is valid (end of sentence will have an
    // invalid tree)
    //
    if (hdptr == (Lex_node*)NULL) {

      // free memory
      //
      delete [] phn;
      phn = (int_4*)NULL;

      // update ref counts
      //
      trace_a->decr_ref_cc();

      // return gracefully
      //
      return ISIP_TRUE;
    }

    // if this is cross-word mode
    //
    if (context_mode_d == DEC_CROSS_WORD_MODE) {

      // if the current word does not end in sil or sp
      //
      if (mid_phn > (int_4)0) {
	
	// set the middle phone of the triphone
	//
	phn[1] = cd_models_d[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]);
	str_lex = trace_a->get_lex_next_cc();
	if (str_lex == (Lex_node*)NULL) {
	  str_lex = hdptr->get_node_cc(phn[1]);
	}
	
	// check if this requires an LM change
	//
	if (str_lex->get_type_cc() != lextype) {
	  switch_lm_cc(trace_a, curr_lex);
	}

	// otherwise grow phones
	//
	else {
	  
	  // get the list of next phones from the current lexical tree
	  //
	  Link_list* lex_list = str_lex->get_child_cc();
	  for (Link_node* nlx = lex_list->get_head_cc();
	       nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
	    
	    // get the child lexical node
	    //
	    lexch = (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_models_cc(trace_a, lexch, phn);
	    
	  } // end loop over all phones
	} // end else grow phones
      } // end if it it cross word mode and word not ending in silence
      
      // otherwise the word ended in sil or sp
      //
      else {
	
	// get the list of all possible next phones
	//
	Link_list* start_list = hdptr->get_child_cc();

	// check if need to switch LM
	//
	if (start_list == (Link_list*)NULL) {
	  switch_lm_cc(trace_a, hdptr);
	}

	// otherwise grow phones
	//
	else {
	  
	  // loop over all next phones
	  //
	  for (Link_node* stx = start_list->get_head_cc();
	       stx != (Link_node*)NULL; stx = stx->get_next_cc()) {
	    
	    // get the child lexical node
	    //
	    str_lex = (Lex_node*)(stx->get_item_cc());
	    phtmp = str_lex->get_phone_cc();
	    
	    // set the middle phone of the triphone
	    //
	    phn[1] = phtmp;
	    
	    // check if this is a no-pronunciation-word end, if so check
	    // if need to switch LM
	    //
	    if (phtmp == LXN_STOP_PHONE) {
	      
	      // make entry in the new LM, grow phones and project traces
	      //
	      switch_lm_cc(trace_a, str_lex);
	    }
	  
	    // otherwise grow phones as normal
	    //
	    else {
	      
	      // get the list of next phones from the current lexical tree
	      //
	      Link_list* lex_list = str_lex->get_child_cc();
	      for (Link_node* nlx = lex_list->get_head_cc();
		   nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
		
		// get the child lexical node
		//
		lexch = (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_models_cc(trace_a, lexch, phn);
		
	      } // end loop over all phones
	    } // end else grow phones
	  } // end loop over all start phones
	} // end else grow phones
      } // end else need to loop over all start phones
    } // if this is cross-word mode

    // otherwise this is word-internal or monophone context
    //
    else {
      
      // get the list of all possible next phones
      //
      Link_list* start_list = hdptr->get_child_cc();

      // check if this is list is null, if so check if need to switch
      // LM
      //
      if (start_list == (Link_list*)NULL) {
	switch_lm_cc(trace_a, hdptr);
      }

      // otherwise grow phones
      //
      else {
	
	// loop over all next phones
	//
	for (Link_node* stx = start_list->get_head_cc();
	     stx != (Link_node*)NULL; stx = stx->get_next_cc()) {
	  
	  // get the child lexical node and the phone there
	  //
	  str_lex = (Lex_node*)(stx->get_item_cc());
	  phtmp = str_lex->get_phone_cc();
	  
	  // check if this is a no-pronunciation-word end, if so check
	  // if need to switch LM
	  //
	  if (phtmp == LXN_STOP_PHONE) {
	    
	    // make entry in the new LM, grow phones and project traces
	    //
	    switch_lm_cc(trace_a, str_lex);
	  }
	  
	  // otherwise if this is word-internal mode, get the next
	  // phone(s)
	  //
	  else if (context_mode_d == DEC_WORD_INTERNAL_MODE) {
	    
	    // set the middle phone of the triphone
	    //
	    phn[1] = phtmp;
	    
	    // get the list of next phones from the current lexical tree
	    //
	    Link_list* lex_list = str_lex->get_child_cc();
	    for (Link_node* nlx = lex_list->get_head_cc();
		 nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
	      
	      // get the child lexical node
	      //
	      lexch = (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_models_cc(trace_a, lexch, phn);
	      
	    } // end loop over all phones
	  } // end if word-internal mode
	  
	  // otherwise it is monophone mode, so this is the next phone
	  //
	  else {
	    
	    // set the phone index
	    //
	    phn[0] = phtmp;
	    
	    // get the word end flag
	    //
	    flag = str_lex->get_word_end_cc();
	    
	    // if possible word end loop over all next nodes
	    //	  
	    if (flag == ISIP_TRUE) {
	      
	      // get the list of next phones from the current lexical
	      // tree
	      //
	      Link_list* lex_list = str_lex->get_child_cc();
	      for (Link_node* nlx = lex_list->get_head_cc();
		   nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
		
		// get the new lex node
		//
		lexch = (Lex_node*)(nlx->get_item_cc());
		
		// if this is a word end node
		//
		if (lexch->get_phone_cc() == LXN_STOP_PHONE) {
		  
		  // grow phone and project traces
		  //
		  grow_models_cc(trace_a, lexch, phn);
		}
	      }
	    } // if this is a sil or sp monophonic word
	    
	    // if not word end process as normal
	    //
	    else {
	      grow_models_cc(trace_a, str_lex, phn);
	    }
	  } // end else monophone mode
	} // end loop over all start phones
      } // end else grow phones
    } // end else this is word-internal or monophone mode
  } // end if start of new word
  
  // otherwise the input trace is phone-level i.e. the next phone
  // belongs to the same word
  //
  else if (curr_lex != (Lex_node*)NULL) {

    // if this is not monophone mode, need to grow context
    //
    if (context_mode_d != DEC_MONO_PHONE_MODE) {
      
      // set the first and middle phone of the triphone
      //
      phn[0] = cd_models_d[curr_phn]->get_phone_cc(mid_phn);
      phn[1] = curr_lex->get_phone_cc();
      
      // if this is not a word-end phone then grow word-internal phones
      //
      if (phn[1] != LXN_STOP_PHONE) {
	
	// get the list of next phones from the current lexical tree
	//
	Link_list* lex_list = curr_lex->get_child_cc();
	for (Link_node* nlx = lex_list->get_head_cc();
	     nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
	  
	  // get the child lexical node
	  //
	  lexch = (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_models_cc(trace_a, lexch, phn);
	}
      } // end if not word end grow word-internal phones
      
      // otherwise need to create the sp phone if this is cross-word
      //
      else if ((context_mode_d == DEC_CROSS_WORD_MODE) && (mid_phn > 0)) {
	
	// create the sp phone
	//
	phn[1] = DEC_SP_PHONE;
	phn[2] = LXN_STOP_PHONE;
	
	// project traces
	//
	grow_models_cc(trace_a, curr_lex, phn);
      }
    } // end if not monophone mode

    // otherwise for monophone mode simply grow the next phone
    //
    else {

      // check the current lex node
      //
      phn[0] = curr_lex->get_phone_cc();

      // make sure it is not an end of word phone
      //
      if (phn[0] != LXN_STOP_PHONE) {
	
	// get the list of next phones from the current lexical tree
	//
	Link_list* lex_list = curr_lex->get_child_cc();
	for (Link_node* nlx = lex_list->get_head_cc();
	     nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
	  
	  // get the child lexical node
	  //
	  lexch = (Lex_node*)(nlx->get_item_cc());
	  
	  // get the word end flag
	  //
	  flag = lexch->get_word_end_cc();

	  // complete the triphone with the last phone
          //
          phn[0] = lexch->get_phone_cc();

	  // if possible word end loop over all next nodes
	  //	  
	  if (flag == ISIP_TRUE) {
	    
	    // get the list of next phones from the current lexical
	    // tree
	    //
	    Link_list* nlex_list = lexch->get_child_cc();
	    for (Link_node* nlx = nlex_list->get_head_cc();
		 nlx != (Link_node*)NULL; nlx = nlx->get_next_cc()) {
	      
	      // get the new lex node
	      //
	      str_lex = (Lex_node*)(nlx->get_item_cc());

	      // if this is a word end node
	      //
	      if (str_lex->get_phone_cc() == LXN_STOP_PHONE) {
		
		// grow phone and project traces
		//
		grow_models_cc(trace_a, str_lex, phn);
	      }
	    }
	  } // end if word end
	  
	  // otherwise grow phones as usual
	  //
	  else {
	    grow_models_cc(trace_a, lexch, phn);
	  }
	} // end for all next lex nodes
      } // if not end of word
    } // end else monophone mode      
  } // end else the next phone is in the same word
  
  // free memory
  //
  delete [] phn;
  phn = (int_4*)NULL;
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
