// file: lat_wer_3.cc
//

// isip include files
//
#include "lattice.h"
#include "lattice_constants.h"

// method: grow_paths_cc
//
// arguments:
//  int_4 nref: (input) number of reference words
//  Word** words: (input) list of reference words
//  Link_list* holder: (input) the list that holds all old paths
//  Link_list** lists: (input) the link lists to save paths
//  Link_node** markers: (input) the link lists markers
//  Lattice_path*& best: (output) the best path at this point
//
// return: an int_4 with number of active lists
//
// this method grows paths and dynamically aligns them with the
// reference transcription
//
int_4 Lattice::grow_paths_cc(int_4 nref_a, Word** words_a,
			     Link_list* holder_a, Link_list** lists_a,
			     Link_node** markers_a, Lattice_path*& best_a) {

  // dummy variables
  //
  int_4 idx = (int_4)0;
  int_4 lidx = (int_4)0;
  int_4 pos = (int_4)0;
  int_4 type = (int_4)0;
  int_4 score = (int_4)0;

  logical_1 best_flag = ISIP_FALSE;
  logical_1 grow_flag = ISIP_FALSE;
  
  Word* lword = (Word*)NULL;
  Lattice_node* latn = (Lattice_node*)NULL;
  Lattice_node* latnode = (Lattice_node*)NULL;
  Link_list* lat_list = (Link_list*)NULL;

  // memory manager
  //
  Memory_manager* manager = Link_list::get_manager_cc();
  
  // lattice paths
  //
  Lattice_path* newpath = (Lattice_path*)NULL;
  Lattice_path* lpath = (Lattice_path*)NULL;
 
  // link nodes
  //
  Link_node* nd = (Link_node*)NULL;
  Link_node* pd = (Link_node*)NULL;
  
  // marker nodes for active lists
  //
  int_4 count = (int_4)0;
  int_4* active = new int_4[num_nodes_d];
  for (int_4 i = 0; i < num_nodes_d; i++) {
    active[i] = (int_4)-1;
    markers_a[i] = (Link_node*)NULL;
    if (lists_a[i] != (Link_list*)NULL) {
      markers_a[i] = lists_a[i]->get_curr_cc();
      if (markers_a[i] != (Link_node*)NULL) {
	active[count++] = i;
      }
    }
  }

  // loop over all active link lists
  //
  for (int_4 i = 0; i < count; i++) {

    // break if a best path is found
    //
    if (best_flag == ISIP_TRUE) {
      break;
    }
    
    // loop over all nodes in the list
    //
    for (nd = lists_a[active[i]]->get_head_cc(); nd != (Link_node*)NULL;
	 nd = pd) {
      
      // get the next node
      //
      pd = nd->get_next_cc();
      
      // get the lattice path and mark it as valid for growing
      //
      lpath = (Lattice_path*)(nd->get_item_cc());
      grow_flag = ISIP_FALSE;

      // lattice path parameters
      //
      pos = lpath->get_pos_cc();
      type = lpath->get_type_cc();
      score = lpath->get_score_cc();
      
      // get the current lattice node
      //
      latnode = lpath->get_curr_cc();
      lidx = latnode->get_node_index_cc();
      
      // a sentence end must match with the reference sentence
      // end, so no need to grow further if all words in the
      // reference are covered
      //
      if (pos < nref_a - 1) {

	// get the next possible lattice nodes
	//
	lat_list = latnode->get_next_nodes_cc();
	
	// make sure this lattice node can be grown further
	//
	if (lat_list != (Link_list*)NULL) {
	  
	  // loop over all next lattice nodes
	  //
	  for (Link_node* lnd = lat_list->get_head_cc();
	       lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	    
	    // get the lattice node, the word and the lat node index
	    //
	    latn = (Lattice_node*)(lnd->get_item_cc());
	    lword = latn->get_word_cc();
	    idx = latn->get_node_index_cc();
	    
	    // compare this word with the corresponding word in the
	    // reference list.
	    //
	    if (lword == words_a[pos + 1]) {
	      
	      // this word matches, so grow a new path with type
	      // correct and put it in the correct list
	      //
	      newpath = manager->new_lpath_cc();
	      newpath->project_cc(lpath, latn, LATTICE_PATH_CORRECT);
	      if (best_a != (Lattice_path*)NULL) {
		if (newpath->get_score_cc() >= best_a->get_score_cc()) {
		  if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
		      == ISIP_TRUE) {
		    grow_flag = ISIP_TRUE;
		  }
		}
		else {
		  manager->delete_cc(newpath);
		}
	      }
	      else if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
		       == ISIP_TRUE) {
		grow_flag = ISIP_TRUE;
	      }
	    } // end if words match
	    
	    // if the word is !NULL then align the !NULL to the previous
	    // reference word with no penalty. It will be stripped out
	    // later
	    //
	    else if (lword == null_word_d) {
	      
	      newpath = manager->new_lpath_cc();
	      newpath->project_cc(lpath, latn, LATTICE_PATH_SKIP);
	      if (best_a != (Lattice_path*)NULL) {
		if (newpath->get_score_cc() >= best_a->get_score_cc()) {
		  if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
		      == ISIP_TRUE) {
		    grow_flag = ISIP_TRUE;
		  }
		}
		else {
		  manager->delete_cc(newpath);
		}
	      }
	      else if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
		       == ISIP_TRUE) {
		grow_flag = ISIP_TRUE;
	      }
	    } // end if words match
	    
	    else {
	      
	      // the new word cannot be a sentence end if substitution
	      // or insertion
	      //
	      if (lword != words_a[nref_a - 1]) {
		
		// create a substitution path and put it in the correct
		// list
		//
		newpath = manager->new_lpath_cc();
		newpath->project_cc(lpath, latn, LATTICE_PATH_SUBSTITUTION);
		if (best_a != (Lattice_path*)NULL) {
		  if (newpath->get_score_cc() >= best_a->get_score_cc()) {
		    if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
			== ISIP_TRUE) {
		      grow_flag = ISIP_TRUE;
		    }
		  }
		  else {
		    manager->delete_cc(newpath);
		  }
		}
		else if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
			 == ISIP_TRUE) {
		  grow_flag = ISIP_TRUE;
		}
		
		// create the insertion path and put it in the correct
		// list
		//
		newpath = manager->new_lpath_cc();
		newpath->project_cc(lpath, latn, LATTICE_PATH_INSERTION);
		if (best_a != (Lattice_path*)NULL) {
		  if (newpath->get_score_cc() >= best_a->get_score_cc()) {
		    if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
			== ISIP_TRUE) {
		      grow_flag = ISIP_TRUE;
		    }
		  }
		  else {
		    manager->delete_cc(newpath);
		  }
		}
		else if (add_path_cc(lists_a[idx], markers_a[idx], newpath)
			 == ISIP_TRUE) {
		  grow_flag = ISIP_TRUE;
		}
	      } // end if word is not sentence end
	      
	      // create the deletion path and put it in the correct
	      // list
	      //
	      newpath = manager->new_lpath_cc();
	      newpath->project_cc(lpath, latnode, LATTICE_PATH_DELETION);
	      if (best_a != (Lattice_path*)NULL) {
		if (newpath->get_score_cc() >= best_a->get_score_cc()) {
		  if (add_path_cc(lists_a[lidx], markers_a[lidx], newpath)
		      == ISIP_TRUE) {
		    grow_flag = ISIP_TRUE;
		  }
		}
		else {
		  manager->delete_cc(newpath);
		}
	      }
	      else if (add_path_cc(lists_a[lidx], markers_a[lidx], newpath)
		       == ISIP_TRUE) {
		grow_flag = ISIP_TRUE;
	      }
	    } // end else make subs, ins or dels
	  } // end loop over all next lattice nodes
	} // end if lattice list exists
      } // end if pos is not at end
      
      // otherwise check if this is the best path
      //
      else {
	
	// get the current word
	//
	lword = (Word*)NULL;
	if (latnode != (Lattice_node*)NULL) {
	  lword = latnode->get_word_cc();
	}
	
	// make sure that the utterance ends in sentence end
	//
	if (lword == words_a[nref_a - 1]) {
	  
	  // update the best path
	  //
	  if (best_a == (Lattice_path*)NULL) {
	    best_a = lpath;
	    grow_flag = ISIP_TRUE;
	  }
	  else if (best_a->get_score_cc() < lpath->get_score_cc()) {
	    holder_a->remove_cc(best_a);
	    manager->delete_cc(best_a);
	    best_a = lpath;
	    grow_flag = ISIP_TRUE;
	  }
	
	  // set flag if a correct hypothesis is found
	  //
	  if (best_a->get_score_cc() == (int_4)0) {
	    best_flag = ISIP_TRUE;
	  }
	} // end if word is sentence end word
      } // end else check best path

      // add this path node to the list of paths to keep
      //
      if (grow_flag == ISIP_TRUE) {
	holder_a->insert_cc(lpath);
      }

      // otherwise remove this path as it cannot grow any further
      //
      else {
	manager->delete_cc(lpath);
      }
      
      // remove this node from the list
      //
      lists_a[active[i]]->remove_cc(nd);
      
      // break if this was the marker node or a best path is found
      //
      if (best_flag == ISIP_TRUE) {
	break;
      }
      if (nd == markers_a[active[i]]) {
	markers_a[active[i]] = (Link_node*)NULL;
	break;
      }
    } // end for loop over nodes
  } // end for all i
  
  // reset count if a best path is found
  //
  if (best_flag == ISIP_TRUE) {
    count = (int_4)0;
  }
  
  // free memory
  //
  delete [] active;
  active = (int_4*)NULL;

  // exit gracefully
  //
  return count;
}
