// file: ht_project_5.cc
//

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

// method: active_trace_cc
//
// arguments:
//  Train_Link_list** phtr_list : (input) list of all phone-level traces
//  Train_Link_node** phmarker : (input) marker position in phone-level trace list
//  Train_Link_node** prev_phmark : (input) previous marker position in phone-level
//                            trace list
//  Train_Link_list** wdtr_list : (input) list of all word-level traces
//  Train_Link_node** wdmarker : (input) marker position in word-level trace list
//  Train_Link_node** prev_wdmark : (input) previous marker position in word-level
//                            trace list
//  int_4* ph_active : (input) list of active phones
//  int_4 ph_numact : (input) number of active phones
//  int_4* wd_active : (input) list of active words
//  int_4 wd_numact : (input) number of active words
//  int_4*& ngen : (output) number of traces generated
//  int_4*& ndel : (output) number of traces deleted
//  float_8 sp : (input) the score for skipping the sp phone
//  float_8& max_score : (output) the maximum path score so far
//
// return: a logical_1 indicating status
//
// this method loops over all phone and word level traces and removes
// all inactive traces, it also creates word-level traces for newly
// completed words
//
logical_1 active_trace_cc(Train_Link_list** phtr_list_a, Train_Link_node** phmarker_a,
			  Train_Link_node** prev_phmark_a, Train_Link_list** wdtr_list_a,
			  Train_Link_node** wdmarker_a, Train_Link_node** prev_wdmark_a,
			  int_4* ph_active_a, int_4 ph_numact_a,
			  int_4* wd_active_a, int_4 wd_numact_a,
			  float_8 sp_a, float_4 penalty_a,
			  int_4*& ngen_a, int_4*& ndel_a, 
			  float_8& max_score_a) {
  
  // local variables 
  //
  Train_Trace* trace = (Train_Trace*)NULL;
  Train_Link_node* nd = (Train_Link_node*)NULL;

  // variables to grow trace
  //
  int_4 ind = (int_4)0;
  int_4 phone = (int_4)0;
  float_8 prune_sc = (float_8)0;
  Train_Trace* tr = (Train_Trace*)NULL;
  Train_Word* word = (Train_Word*)NULL;
  Train_Lattice_node* latnode = (Train_Lattice_node*)NULL;
  Train_Lex_node* lexnode = (Train_Lex_node*)NULL;
  Train_Lex_node* newlex = (Train_Lex_node*)NULL;
  
  // memory manager
  //
  Train_Memory_manager* manager = Train_Link_list::get_manager_cc();
  
  // loop over all active word-level trace lists
  //
  for (int_4 ww = 0; ww < wd_numact_a; ww++) {
  
    // loop over all the previous word-level traces and remove the
    // traces that are no longer active
    //
    for (Train_Link_node* node = prev_wdmark_a[wd_active_a[ww]];
	 (node != (Train_Link_node*)NULL) && (node != wdmarker_a[wd_active_a[ww]]);
	 node = nd) {
      
      // get the next node
      //
      nd = node->get_next_cc();
      
      // get the trace in this node
      //
      trace = (Train_Trace*)(node->get_item_cc());

      // remove if this trace was not active in the current frame
      //
      if (trace->get_active_cc() == ISIP_FALSE) {

	// decrement the appropriate count
	//
	ndel_a[HT_WORD_LEVEL]++;

	// delete the trace
	//
      	manager->delete_cc(trace);
	wdtr_list_a[wd_active_a[ww]]->remove_cc(node);
      }
      
      // otherwise reset the active flag
      //
      else {
	trace->set_active_cc(ISIP_FALSE);
      }
    } // end for loop over all nodes
  } // end for loop over all active words
    
  // loop over all active phone-level trace lists
  //
  for (int_4 pp = 0; pp < ph_numact_a; pp++) {
  
    // loop over all the previous phone-level traces and remove the
    // traces that are no longer active
    //
    for (Train_Link_node* node = prev_phmark_a[ph_active_a[pp]];
	 (node != (Train_Link_node*)NULL) && (node != phmarker_a[ph_active_a[pp]]);
	 node = nd) {

      // get the next node
      //
      nd = node->get_next_cc();
      
      // get the trace in this node
      //
      trace = (Train_Trace*)(node->get_item_cc());

      // remove if this trace was not active in the current frame
      //
      if (trace->get_active_cc() == ISIP_FALSE) {

	// decrement the appropriate count
	//
	ndel_a[HT_PHONE_LEVEL]++;

	// delete the trace
	//
      	manager->delete_cc(trace);
	phtr_list_a[ph_active_a[pp]]->remove_cc(node);
      }
      
      // otherwise reset the active flag
      //
      else {
	trace->set_active_cc(ISIP_FALSE);
      }
    } // end for loop over all nodes

    // for the currently active phone-level traces at end of word make
    // word-level traces
    //
    if (phtr_list_a[ph_active_a[pp]] != (Train_Link_list*)NULL) {

      // make sure there is at least one active trace
      //
      nd = phtr_list_a[ph_active_a[pp]]->get_curr_cc();
      if (nd != (Train_Link_node*)NULL) {
	
	// now loop over all the active phone-end traces and grow
	// sp-skipped word ends
	//
	for (Train_Link_node* node = phmarker_a[ph_active_a[pp]];
	     node != nd->get_next_cc(); node = node->get_next_cc()) {
	  
	  // get the trace in this node
	  //
	  trace = (Train_Trace*)(node->get_item_cc());
	  tr = (Train_Trace*)NULL;
	  
	  // the current trace parameters
	  //
	  lexnode = trace->get_lex_node_cc();
	  phone = lexnode->get_phone_cc();
	  
	  // if this is an end of word trace create the word-level
	  // trace
	  //
	  if (phone == TRAIN_LXN_STOP_PHONE) {

	    // copy the trace and add word insertion penalty
	    //
	    tr = manager->new_trace_cc();
	    tr->project_trace_cc(trace, penalty_a);

	    // make sure the phone is not sp
	    //
	    tr->set_phone_ind_cc(ph_active_a[pp]);
	  }
	  
	  // otherwise if the word ends in sp phone, create word level
	  // trace corresponding to the sp skip
	  //
	  else if (phone == HT_SP_PHONE) {
	    
	    // copy the trace and add sp skip score and the word
	    // insertion penalty
	    //
	    tr = manager->new_trace_cc();
	    tr->project_trace_cc(trace, sp_a + penalty_a);

	    // make sure the phone is not sp
	    //
	    tr->set_phone_ind_cc(ph_active_a[pp]);

	    // set the correct lexical node
	    //
	    newlex = lexnode->get_node_cc(TRAIN_LXN_STOP_PHONE);
	    tr->set_lex_node_cc(newlex);
	  }

	  // otherwise subtract the lex node score added to this trace
	  // for beam pruning purposes
	  //
	  else {
	    prune_sc = lexnode->get_max_score_cc();
	    trace->incr_score_cc(-prune_sc);
	  }	    

	  // insert the new trace, if any, in the appropriate word list
	  //
	  if (tr != (Train_Trace*)NULL) {
	    
	    // set the trace back-pointer and level index
	    //
	    tr->set_level_cc(HT_WORD_LEVEL);
	    tr->set_back_size_cc(HT_WORD_BACKPTR);
	    tr->add_back_ptr_cc(trace);

	    // find out the active word this belongs to
	    //
	    latnode = trace->get_lat_node_cc();
	    word = latnode->get_word_cc();
	    ind = word->get_index_cc();
	    
	    // insert the new trace in the trace list
	    //
	    insert_trace_cc(wdtr_list_a[ind], tr, wdmarker_a[ind],
			    ngen_a[HT_WORD_LEVEL], max_score_a);

	    // update the word list marker position
	    //
	    if (wdmarker_a[ind] == (Train_Link_node*)NULL) {
	      wdmarker_a[ind] = wdtr_list_a[ind]->get_curr_cc();
	    }
	  }
	} // end for all current link nodes
      } // end if nd is not NULL
    } // end if trace list is not NULL
  } // end for all active phones
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
