// file: ht_project_0.cc
//

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

static int_4* trans_states = (int_4*)NULL;
static float_4* trans_scores = (float_4*)NULL;
static int_4 trans_states_size = 0;

// method: project_states_cc
//
// arguments:
//  Train_Link_list** token_list: (input) linked list of active tokens
//  int_4 frame: (input) current frame
//  float_8* feat: (input) input mfcc data
//  int_4 sp_phone: (input) index of the sp phone
//  Train_Link_list** phtr_list: (input) linked list of active phone-level traces
//  int_4* ph_active: (input) list of active phones
//  int_4 ph_numact: (input) number of active phones
//  int_4 align_mode: (input) state/phone alignment
//  Train_Phone** phones: (input) phone models
//  int_4*& ngen: (output) number of traces generated
//  int_4*& ndel: (output) number of traces deleted
//  int_4& napmi: (output) number of active phone-level traces
//  float_8*& score: (output) maximum path scores so far at each level
//  Train_Link_node** ph_marker: (output) the new phone-level trace marker position
//
// return: a logical_1 indicating status
//
// this method loops over all the active states, evaluates them and
// projects traces to the transitions with updated scores
//
logical_1 project_states_cc(Train_Link_list** token_list_a, int_4 frame_a,
			    float_8* feat_a, int_4 sp_phone_a,
			    Train_Link_list** phtr_list_a, int_4* ph_active_a,
			    int_4 ph_numact_a, int_4 align_mode_a,
			    Train_Phone** phones_a, int_4*& ngen_a,
			    int_4*& ndel_a, int_4& napmi_a,
			    float_8*& score_a, Train_Link_node** ph_marker_a) {

  // dummy variables
  //
  Train_Link_list* trlist = (Train_Link_list*)NULL;
  Train_Link_node* node = (Train_Link_node*)NULL;
  Train_Link_node* nd = (Train_Link_node*)NULL;
  Train_Link_node* mark = (Train_Link_node*)NULL;
  
  Train_Trace* tr = (Train_Trace*)NULL;
  Train_Trace* new_trace = (Train_Trace*)NULL;
  Train_Trace** back = (Train_Trace**)NULL;
  Train_Trace** back1 = (Train_Trace**)NULL;

  int_4 num = (int_4)0;
  int_4 num1 = (int_4)0;
  
  Train_Token* tok = (Train_Token*)NULL;
  Train_Model* model = (Train_Model*)NULL;

  // state transition parameters
  //
  int_4 num_trans = (int_4)0;

  // position flags and other params
  //
  logical_1 pos_flag = ISIP_FALSE;
  float_8 score = (float_8)0;
  int_4 ref = (int_4)0;
  int_4 num_ph = (int_4)0;
  int_4 num_st = (int_4)0;
  int_4 st = (int_4)0;
  int_4 phn = (int_4)0;
  int_4 ii = (int_4)0;

  // the index of the highest mixture for TRAIN
  //
  int_4 max_mix = (int_4)0;

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

  // loop over all the phones
  //
  for (int_4 pp = 0; pp < ph_numact_a; pp++) {

    // set the active phone index and the corresponding trace list index
    //
    ii = ph_active_a[pp];
    phn = ii;

    // make sure the tokens exist for this phone
    //
    if (token_list_a[ii] != (Train_Link_list*)NULL) {
    
      // get the model params for this phone
      //
      model = phones_a[ii]->get_model_cc();
      num_st = model->get_num_states_cc();
      num_ph = phones_a[ii]->get_num_phones_cc();
      
      // loop over all the active tokens --- each corresponds to an
      // active state for this phone
      //
      for (Train_Link_node* tok_node = token_list_a[ii]->get_head_cc();
	   tok_node != (Train_Link_node*)NULL;
	   tok_node = tok_node->get_next_cc()) {
	
	// get the corresponding token
	//
	tok = (Train_Token*)(tok_node->get_item_cc());
	
	// find the position ahead of which to add projected traces
	//
	mark = tok->get_marker_cc();
	
	// check if we need to evaluate this state here
	//
	if (mark != (Train_Link_node*)NULL) {
	  
	  // evaluate the state
	  //
	  max_mix = (int_4)0;
	  st = tok->get_state_cc();
	  score = model->get_output_prob_cc(st, feat_a, frame_a, max_mix);
	  tok->set_score_cc(score);
	  
	  // find the possible transition states from here
	  //
	  model->get_next_states_cc(st, num_trans, trans_states,
				    trans_scores, trans_states_size);
	  
	  // set the position flag
	  //
	  pos_flag = ISIP_FALSE;
	  
	  // get all the traces in this token
	  //
	  trlist = tok->get_trace_list_cc();

	  // get the start position for the new traces
	  //
	  nd = tok->get_prev_marker_cc();
	  
	  // loop over all the traces to grow
	  //
	  while ((nd != (Train_Link_node*)NULL) && (pos_flag == ISIP_FALSE)) {
	    
	    // check position flag
	    //
	    if (nd == mark) {
	      pos_flag = ISIP_TRUE;
	    }
	    
	    // get the next trace to go to
	    //
	    node = nd->get_next_cc();
	    
	    // get the trace in the current node
	    //
	    tr = (Train_Trace*)(nd->get_item_cc());

	    // set the maximum mixture index
	    //
	    tr->set_max_mixture_cc(max_mix);
	    
	    // increment its score and frame index
	    //
	    tr->incr_score_cc(score);
	    tr->incr_frame_ind_cc();
	    
	    /*---
	      Need to exercise care here --- if state-level alignment
	      is desired, then we need to find the previous
	      phone-level trace on its path and set that as active, as
	      the immediate back-pointer could be state level. This is
	      even more significant (and not handled by the following
	      code) if the number of state-level back-pointers is
	      larger than 1.
	      ---*/

	    // get its backpointer and mark it as active for this frame
	    //
	    back = tr->get_back_ptr_cc();
	    num = tr->get_back_size_cc();
	    while (back[0]->get_level_cc() == HT_STATE_LEVEL) {
	      back = back[0]->get_back_ptr_cc();
	      num = back[0]->get_back_size_cc();
	    }
	    
	    for (int_4 i = 0; (i < num) && (back[i] != (Train_Trace*)NULL); i++) {
	      if (back[i]->get_active_cc() == ISIP_FALSE) {
		back[i]->set_active_cc(ISIP_TRUE);
		
		// if this is a word-level trace also need to activate
		// previous phone-level traces
		//
		if (back[i]->get_level_cc() == HT_WORD_LEVEL) {

		  // get its backpointer and mark it as active for
		  // this frame
		  //
		  back1 = back[i]->get_back_ptr_cc();
		  if (back1 != (Train_Trace**)NULL) {
		    num1 = back[i]->get_back_size_cc();
		    for (int_4 j = 0; (j < num1) && (back1[j] != (Train_Trace*)NULL);
			 j++) {
		      if (back1[j]->get_active_cc() == ISIP_FALSE) {
			back1[j]->set_active_cc(ISIP_TRUE);
		      }
		    } // end for j
		  } // end if not null		  
		} // end if word level
	      } // end if not active
	    } // end for i
	    
	    // if the current phone is sp then the trace index is
	    // different
	    //
	    if (ii == sp_phone_a) {
	      phn = back[0]->get_phone_ind_cc();
	    }
	    
	    // loop over all of the possible next states and pass traces
	    // to each
	    //
	    for (int_4 kk = 0; kk < num_trans; kk++) {
	      
	      // create a new trace with the transitioned state
	      //
	      new_trace = manager->new_trace_cc();
	      new_trace->project_trace_cc(tr, (float_8)trans_scores[kk]);
	      new_trace->set_state_ind_cc(trans_states[kk]);
	      
	      // if this transition is at the end of phone i.e. goes to
	      // the exit state pass it to the active traces list
	      //
	      if (trans_states[kk] == num_st - (int_4)1) {

		// copy the backpointer
		//
		new_trace->set_level_cc(HT_PHONE_LEVEL);
		new_trace->set_back_size_cc(HT_PHONE_BACKPTR);

		// if state level alignments needed, point to the trace
		// creating this new trace
		//
		if (align_mode_a == HT_STATE_ALIGN) {
		  new_trace->add_back_ptr_cc(tr);
		}

		// point to the back trace of the trace creating this new
		// trace
		//
		else {
		  new_trace->set_back_ptr_cc(back);
		}
		
		// for end of phone update the phone-level score and
		// put this in the active traces list
		//
		insert_trace_cc(phtr_list_a[phn], new_trace, ph_marker_a[phn],
				ref, ngen_a[HT_PHONE_LEVEL], napmi_a,
				score_a[HT_PHONE_LEVEL]);
		
		// update the positions in the trace list
		//
		if (ph_marker_a[phn] == (Train_Link_node*)NULL) {
		  ph_marker_a[phn] = phtr_list_a[phn]->get_curr_cc();
		}
	      } // end if phone-end loop
	      
	      // otherwise this is a model-internal transition
	      //
	      else {

		// set the state level and backpointer
		//
		new_trace->set_level_cc(HT_STATE_LEVEL);
		new_trace->set_back_size_cc(HT_STATE_BACKPTR);

		// if state level alignments needed, point to the trace
		// creating this new trace
		//
		if (align_mode_a == HT_STATE_ALIGN) {
		  new_trace->add_back_ptr_cc(tr);
		}

		// point to the back trace of the trace creating this new
		// trace
		//
		else {
		  new_trace->set_back_ptr_cc(back);
		}
				
		// put the trace in the appropriate token updating the
		// state level max score
		//
		insert_token_cc(token_list_a[ii], new_trace,
				trans_states[kk], ngen_a[HT_STATE_LEVEL],
				score_a[HT_STATE_LEVEL]);
	      }
	    } // end for state transitions loop

	    // free memory held in this node
	    //
	    if (align_mode_a == HT_MODEL_ALIGN) {
	      manager->delete_cc(tr);
	      trlist->remove_cc(nd);
	      ndel_a[HT_STATE_LEVEL]++;
	    }
	    
	    // set new node pointer
	    //
	    nd = node;
	    
	  } // end while loop
	} // end if token needs to be eval loop

	// set the new marker position
	//
	if (align_mode_a == HT_MODEL_ALIGN) {
	  tok->set_marker_cc((Train_Link_node*)NULL);
	}
	
      } // end all tokens loop
    } // end if token list is not null
  } // end all phones loop

  // exit gracefully
  //
  return ISIP_TRUE;
}
