// file: $isip/class/search/StackSearch/ssrch_05.cc
// version: $Id: ssrch_05.cc 9173 2003-05-30 02:02:08Z jelinek $
//

// isip include files
//
#include "StackSearch.h"

// method: getHypotheses
//
// arguments:
//  String& output_hyp: (output) the current search hypotheses
//  long level: (input) the level to print hypotheses from
//  double& total_score: (output) the hypothesis total score
//  long& num_frames: (output) frame index of the last trace
//  DoubleLinkedList<Trace>& trace_path: (output) best hypothesis trace path
//  String* word_hyp: (output) word level hypothesis
//
// return: logical error status
//
// build a graph representing the hypotheses and return it
//
boolean StackSearch::getHypotheses(String& output_hyp_a,
				   long level_a,
				   double& total_score_a,
				   long& num_frames_a,
				   DoubleLinkedList<Trace>& trace_path_a,
				   String* word_hyp_a) {

  if (debug_level_d >= Integral::ALL) {
    Console::put(L"\nGetting the hypotheses ...\n\n");
  }
  
  // declare local variables
  //
  Trace* tmp_trace = (Trace*)NULL;
  SearchNode*  prev_node = (SearchNode*)NULL;
  SearchNode*  tmp_node = (SearchNode*)NULL;
  
  long counter = 0;
  long curr_level = 0;
  long frame_ind = 0;
  long wframe_ind = 0;
  long prev_frame_ind = -1;
  long wprev_frame_ind = -1;
  long back_count = 0;
  float score = 0.0;
  float prev_score = 0.0;
  
  String out_str;
  SearchSymbol sym;  
  
  // clear the output and set the allocation mode for the trace path
  //
  output_hyp_a.clear();

  trace_path_a.clear();
  trace_path_a.setAllocationMode(DstrBase::USER);
  
  // move all traces forward in the search space
  //
  if (stack_level_d < (long)1) {
    if (!((decoding_mode_d == GLOBAL_STACK) && (stack_level_d == (long)0))) {
      if (!propagateTraces()) {
	// cleanup after the utterance
	//
	// clear the stacks
	//
	for (long i = 0; i < stacks_d.length(); i++) {
	  stacks_d(i).clear();
	}
	if (decoding_mode_d == GLOBAL_STACK) {
	  if (debug_level_d >= Integral::BRIEF) {
	    Console::put(L"going to clear glob stack, capacity: ");
	    String cap = global_stack_d.getCapacity();
	    Console::put(cap);
	  }
	  while (!global_stack_d.isEmpty()) {
	    Hypothesis hyp;
	    global_stack_d.pop(&hyp);
	    Trace* tr = hyp.getTrace();
	    Trace::deleteTrace(tr, true, true);
	  }
	}

	// clean state traces
	//
	DoubleLinkedList<Trace> state_traces(DstrBase::USER);
	getTraces(state_traces, getNumLevels() - 1);

	Trace* curr_trace = NULL;
	while (state_traces.removeFirst(curr_trace)) {
	  Trace::deleteTrace(curr_trace, true);
	}
  
	// clear valid hypotheses
	//
	clearValidHypsTrace();

	// exit ungracefully
	//
	return false;
      }
    }
  }

  // make sure we have at least one valid hypothesis
  //  
  if (valid_hyps_d.length() < 1) {

    // output the debugging information
    //
    if (debug_level_d >= Integral::ALL) {
      Console::put(L"no valid hypotheses found, cleaning");
    }
    
    // cleanup after the utterance
    //
    // clear the stacks
    //
    for (long i = 0; i < stacks_d.length(); i++) {
      stacks_d(i).clear();
    }

    // clean state traces
    //
    DoubleLinkedList<Trace> state_traces(DstrBase::USER);
    getTraces(state_traces, getNumLevels() - 1);

    Trace* curr_trace = NULL;
    while (state_traces.removeFirst(curr_trace)) {
      Trace::deleteTrace(curr_trace, true);
    }
  
    // clear valid hypotheses
    //
    clearValidHypsTrace();

    // exit ungracefully
    //
    return false;
  }

  // pick up the best valid hypothesis
  //
  Trace* best_end_hyp = valid_hyps_d.pop();

  if (debug_level_d >= Integral::ALL) {
    String out(L"Best hypothesis: ");
    out.concat(best_end_hyp);
    Console::put(out);
  }

  // backtrack from the best hypothesis trace and generate the trace path
  //
  for (tmp_trace = best_end_hyp; tmp_trace != (Trace*)NULL;
       tmp_trace = tmp_trace->getBackPointer()) {
    trace_path_a.insertFirst(tmp_trace);
    back_count++;
  }

  // use the best hypothesis trace path to generate the hypothesis string
  //
  for (boolean more_traces = trace_path_a.gotoFirst(); more_traces;
       more_traces = trace_path_a.gotoNext()) {

    counter++;
    tmp_trace = trace_path_a.getCurr();
    frame_ind = tmp_trace->getFrame();
    wframe_ind = frame_ind;
    
    // get the central vertex
    //
    GraphVertex<SearchNode>* tmp_vertex =
      tmp_trace->getSymbol()->getCentralVertex();

    // check for a NULL vertex
    //
    if (tmp_vertex == (GraphVertex<SearchNode>*)NULL) {
      return Error::handle(name(), L"getHypotheses - NULL VERTEX",
			   Error::ARG, __FILE__, __LINE__);
    }
    
    // make sure the vertex is neither the dummy
    // start nor terminating node
    //
    if ((!tmp_vertex->isStart()) &&
	(!tmp_vertex->isTerm())) {

      tmp_node = tmp_vertex->getItem();
      curr_level = tmp_node->getSearchLevel()->getLevelIndex();
      
      if ((level_a < 0) && (curr_level == 0)) {
	
	tmp_node->getSymbol(sym);
	score = tmp_trace->getScore();
	long symbol_id = tmp_node->getSymbolId();
	
	// generate the output hypothesis containing only search symbols
	//
	if (!getSearchLevel(curr_level).isExcludeSymbol(symbol_id)) {
	  out_str.assign(sym);
	  out_str.concat(L" ");
	}
	else {
	  out_str.assign(L"");
	}
	
	// append partial string to the complete hypothesis report
	//
	if ((frame_ind != prev_frame_ind) && (frame_ind != 0)) {
	  output_hyp_a.concat(out_str);
	}
      }
      
      if ((curr_level == 0) && (word_hyp_a != NULL)) {

	SearchSymbol ss;
	tmp_node->getSymbol(ss);
	
	// generate the output hypothesis containing only search symbols
	//
	String out;
	if (sym.ne(L"!SENT_START") && sym.ne(L"!SENT_END")) {
	  out.assign(ss);
	  out.concat(L" ");
	}
	else {
	  out.assign(L"");
	}
	
	// append partial string to the complete hypothesis report
	//
	if ((wframe_ind != wprev_frame_ind) && (wframe_ind != 0)) {
	  word_hyp_a->concat(out);
	}
	wprev_frame_ind = wframe_ind;
      }
      
      if ((level_a >= 0) && (curr_level == level_a)) {

	String context;
	tmp_trace->getSymbol()->print(context);
	score = tmp_trace->getScore();
	sym.assign(context);

	// generate the output hypothesis
	//
	if (counter > 1) {
	  out_str.assign(L"\n");
	}
	else {
	  out_str.assign(L"");
	}

	// append previous frame index
	//
	if (level_a != (getNumLevels() - 1)) {
	  out_str.concat(prev_frame_ind);
	}
	else {
	  out_str.concat(prev_frame_ind + 1);
	}
	out_str.concat(L"\t");

	// append current frame index
	//
	if (level_a != (getNumLevels() - 1)) {
	  out_str.concat(frame_ind);
	}
	else {
	  out_str.concat(frame_ind + 1);
	}
	out_str.concat(L"\t");
	
	// append search symbol
	//
	out_str.concat(sym);
	out_str.concat(L"\t\t");
	
	// append score
	//
	out_str.concat(score - prev_score);
	
	// append partial string to the complete hypothesis report
	//
	if (level_a == (getNumLevels() - 1)) {
	  output_hyp_a.concat(out_str);
	}
	else {
	  if ((frame_ind != prev_frame_ind) && (frame_ind != 0)) {
	    output_hyp_a.concat(out_str);
	  }
	}
      }
      if (((level_a < 0) && (curr_level == 0)) ||
	  ((level_a >= 0) && (curr_level == level_a))) {
	prev_node = tmp_node;
	prev_score = score;
	prev_frame_ind = frame_ind;
      }
    }
  }

  if ((evaluation_mode_d == SEGMENT_EVAL) &&
      (level_a == getNumLevels() - 1)) {
    score = best_end_hyp->getScore();
    output_hyp_a.concat(L"\n\t\t\t\t");
    output_hyp_a.concat(score - prev_score);
  }

  total_score_a = score;
  num_frames_a = frame_ind;

  Trace::deleteTrace(best_end_hyp, true, true);
  
  // cleanup after the utterance
  //
  // clear the stacks
  //
  for (long i = 0; i < stacks_d.length(); i++) {
    stacks_d(i).clear();
  }
  if (decoding_mode_d == GLOBAL_STACK) {
    if (debug_level_d >= Integral::BRIEF) {
      Console::put(L"going to clear glob stack, capacity: ");
      String cap = global_stack_d.getCapacity();
      Console::put(cap);
    }
    while (!global_stack_d.isEmpty()) {
      Hypothesis hyp;
      global_stack_d.pop(&hyp);
      Trace* tr = hyp.getTrace();
      Trace::deleteTrace(tr, true, true);
    }
  }

  // clean state traces
  //
  DoubleLinkedList<Trace> state_traces(DstrBase::USER);
  getTraces(state_traces, getNumLevels() - 1);

  Trace* curr_trace = NULL;
  while (state_traces.removeFirst(curr_trace)) {
    Trace::deleteTrace(curr_trace, true);
  }
  
  // clear valid hypotheses
  //
  clearValidHypsTrace();

  // output statistics
  //
  if (debug_level_d >= Integral::BRIEF) {
    String out(L"\nFrame: ");
    out.concat(L" Traces generated: ");
    out.concat(L"  Vit. pruned: ");
    out.concat(L"  beam pruned: ");
    out.concat(L"  evaluated: ");
    out.concat(L"  avoided eval: ");
    Console::put(out);

    long num_traces_gen = 0;
    long num_traces_vit_prun = 0;
    long num_traces_beam_prun = 0;
    long num_eval = 0;
    long num_avoided_eval = 0;
  
    for (long i = 0; i < num_frames_d; i++) {
      out.assign(i);
      out.concat(L"  ");
    
      out.concat(num_traces_gen_d(i));
      num_traces_gen += num_traces_gen_d(i);
      out.concat(L"  ");
    
      out.concat(num_traces_vit_prun_d(i));
      num_traces_vit_prun += num_traces_vit_prun_d(i);
      out.concat(L"  ");
    
      out.concat(num_traces_beam_prun_d(i));
      num_traces_beam_prun += num_traces_beam_prun_d(i);
      out.concat(L"  ");
    
      out.concat(num_eval_d(i));
      num_eval += num_eval_d(i);
      out.concat(L"  ");
    
      out.concat(num_avoided_eval_d(i));
      num_avoided_eval += num_avoided_eval_d(i);
      Console::put(out);
    }

    out.assign(L"Total: ");
    out.concat(num_traces_gen);
    out.concat(L" ");
    out.concat(num_traces_vit_prun);
    out.concat(L" ");
    out.concat(num_traces_beam_prun);
    out.concat(L" ");
    out.concat(num_eval);
    out.concat(L" ");
    out.concat(num_avoided_eval);
    out.concat(L" num stat mod: ");
    out.concat(stat_model_mapping_d.length());
    Console::put(out);
  }

  // exit gracefully
  //
  return true;
}

// method: printNewPath
//
// arguments:
//  Trace* new_trace: (input) the endpoint of the new arc
//  Trace* old_trace: (input) the start point of the new arc
//
// return: logical error status
//
// print an arc created in the hypothesis space
//
boolean StackSearch::printNewPath(Trace* new_trace_a,
				  Trace* old_trace_a) {

  SearchSymbol start_sym;
  SearchSymbol end_sym;
  
  float old_score = 0.0;
  float new_score = 0.0;

  long old_frame = -1;
  long new_frame = -1;
  
  if (old_trace_a != (Trace*)NULL) {
    SearchNode* old_node = old_trace_a->getSymbol()->getCentralVertex()->getItem();
    old_frame = old_trace_a->getFrame();   
    if ( old_node == &DiGraph<SearchNode>::START_OBJ) {
      start_sym.assign(L"_START_");
    }
    else if (old_node == &DiGraph<SearchNode>::TERM_OBJ) {
      start_sym.assign(L"_TERM_");
    }
    else {
      old_node->getSymbol(start_sym);
    }
    old_score = old_trace_a->getScore();
  }

  if (new_trace_a != (Trace*)NULL) {
    SearchNode* new_node = new_trace_a->getSymbol()->getCentralVertex()->getItem();
    new_frame = new_trace_a->getFrame();
    if (new_node == &DiGraph<SearchNode>::START_OBJ) {
      end_sym.assign(L"_START_");
    }
    else if (new_node == &DiGraph<SearchNode>::TERM_OBJ) {
      end_sym.assign(L"_TERM_");
    }
    else {
      new_node->getSymbol(end_sym);
    }
    new_score = new_trace_a->getScore();
  }
  
  String out(L" oTR ");
  out.concat(old_trace_a);
  out.concat(L" [");
  out.concat(start_sym);
  out.concat(L"] -> [");
  out.concat(end_sym);
  out.concat(L"] nTR ");
  out.concat(new_trace_a);
  out.concat(L" oSC: ");
  out.concat(old_score);
  out.concat(L" -> nSC: ");
  out.concat(new_score);
  out.concat(L" FR ");
  out.concat(new_frame);
  Console::put(out);
  
  return true;
}

// method: printDeletedPath
//
// arguments:
//  Trace* new_trace: (input) the endpoint of the new arc
//  Trace* old_trace: (input) the start point of the new arc
//
// return: logical error status
//
// print an arc created in the hypothesis space
//
boolean StackSearch::printDeletedPath(Trace* new_trace_a,
				      Trace* old_trace_a) {

  SearchSymbol start_sym;
  SearchSymbol end_sym;

  long old_frame = -1;
  long new_frame = -1;
  
  if (old_trace_a != (Trace*)NULL) {
    SearchNode* old_node = old_trace_a->getSymbol()->getCentralVertex()->getItem();
    old_frame = old_trace_a->getFrame();    
    if ( old_node == &DiGraph<SearchNode>::START_OBJ) {
      start_sym.assign(L"_START_");
    }
    else if (old_node == &DiGraph<SearchNode>::TERM_OBJ) {
      start_sym.assign(L"_TERM_");
    }
    else {
      old_node->getSymbol(start_sym);
    }
  }

  if (new_trace_a != (Trace*)NULL) {

    SearchNode* new_node = new_trace_a->getSymbol()->getCentralVertex()->getItem();
    new_frame = new_trace_a->getFrame();
    if (new_node == &DiGraph<SearchNode>::START_OBJ) {
      end_sym.assign(L"_START_");
    }
    else if (new_node == &DiGraph<SearchNode>::TERM_OBJ) {
      end_sym.assign(L"_TERM_");
    }
    else {
      new_node->getSymbol(end_sym);
    }
  }
  
  String out(L"deleted trace ");
  out.concat(new_trace_a);
  out.concat(L" generated from [");
  out.concat(start_sym);
  out.concat(L"] in frame ");
  String val;
  val.assign((long)old_frame);  
  out.concat(val);
  out.concat(L" to [");
  out.concat(end_sym);
  out.concat(L"] in frame ");
  val.assign((long)new_frame);  
  out.concat(val);
  Console::put(out);
  
  return true;
}

