// file: lat_write_0.cc
//

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

// method: write_lattice_cc
//
// arguments:
//
//  FILE* fp_out_a: (output) output lattice file
//  logical_1 probflag: (input) input flag indicating whether lmscore is
//                       lmscore(lattice rescoring mode)
//                       or probabilities (grammarcompiler)
//  logical_1 probablistic_flag: (input) input flag indicating whether to keep
//                               equiprobable probabilities as zero for
//                               backward compatibility or replace it with
//                               equiprobable probabilities(grammarcompiler)
//  logical_1 allprob_flag: (input) input flag indicating that probabilities
//                          are specified in the grammar 
//   
//
// return: a logical_1 indicating status
//
// this method write a lattice into a file
//
logical_1 Lattice::write_lattice_cc(FILE* fp_lat_a, logical_1 probflag_a,
				    logical_1 probablistic_flag_a,
				    logical_1 allprob_flag_a) {
  
  // local variables
  //
  Lattice_node* start_latn = start_node_d;
  Lattice_node* end_latn = (Lattice_node*)NULL;
  Link_list* nodes_list = (Link_list*)NULL;
  
  // initialize a list of lattice nodes
  //
  Lattice_node** lnodes = (Lattice_node**)NULL;
  lat_arrange_cc(lnodes);
  
  // print the header if required
  //
  fprintf(fp_lat_a, "VERSION=1.0\n");
  
  // print the utterance id if available
  //
  if (utterance_d != (char_1*)NULL) {
    fprintf(fp_lat_a, "UTTERANCE=%s\n", utterance_d);
  }

  // print the language model scale factor and the word insertion
  // penalty
  //
  fprintf(fp_lat_a, "lmscale=%.2f\n", lm_scale_d);
  fprintf(fp_lat_a, "wdpenalty=%.2f\n", word_penalty_d);

  // write the language model and acoustic model used
  //
  if (lang_model_d != (char_1*)NULL) {
    fprintf(fp_lat_a, "vocab=%s\n", lang_model_d);
  }
  if (model_set_d != (char_1*)NULL) {
    fprintf(fp_lat_a, "hmms=%s\n", model_set_d);
  }

  // print the number of nodes and arcs in the lattice
  //
  fprintf(fp_lat_a, "N=%-8ld  L=%-8ld\n", num_nodes_d, num_arcs_d);
  
  // print out the nodes information
  //
  float_4 frame_idx = (float_4)0; 
  for (int_4 i = 0; i < num_nodes_d; i++) {
    frame_idx = lnodes[i]->get_frame_index_cc() * LATTICE_FRAME_DURATION;
    fprintf(fp_lat_a, "I=%-8ld t=%-.2f\n", i, frame_idx);
  }

  // print out the arcs information
  // 
  int_4 count = (int_4)0;
  int_4 arc_count = (int_4)0;
  int_4 num_arcs_out = (int_4)0;
  float_4* lm_scores = (float_4*)NULL;
  float_4* lm_scoresup = (float_4*)NULL;
  float_4* ac_scores = (float_4*)NULL;
  int_4 num = (int_4)0;
  logical_1 unknown = ISIP_FALSE;
  
  // loop over all the nodes to find the unknown equiprobable probabilities
  // 
  for (int_4 i = 0; i < num_nodes_d; i++) {

    // local variables
    //
    float_4 prob = (float_4)0;
    float_4 sum = (float_4)0;
    unknown = ISIP_FALSE;
    
    // set the start node for this arc
    //
    start_latn = lnodes[i];

    // get the next nodes list
    //
    nodes_list = start_latn->get_next_nodes_cc();
    
    // if this is not the last node in the lattice
    //
    if (nodes_list != (Link_list*)NULL) {

      // get the start node information
      //
      num_arcs_out = start_latn->get_num_arcs_out_cc();
      lm_scores = start_latn->get_lm_scores_cc();
      ac_scores = start_latn->get_ac_scores_cc();
      
      // do probabilities tests and calculations only if lmscore represents
      // probabilities when this function is called by the grammar compiler
      //
      if (probflag_a) {
	
	// loop over all the next nodes to get the sum of probalitities
	// of all the words emanating form this node
	//
	count = (int_4)0;
	num = (int_4)0;
	for (Link_node* lnd = nodes_list->get_head_cc();
	     lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	  
	  // sum of probabilities of all outgoing arcs
	  //
	  sum += lm_scores[count];
	  
	  if (lm_scores[count] == (float_4)0) {
	    unknown = ISIP_TRUE;
	    num ++;
	  }
	  
	  // increment count
	  //
	  count++;
	}
	
	// other arcs are equally probable
	//
	if (unknown) {
	  if((!probablistic_flag_a) && (!allprob_flag_a)) {
	    prob = (int_4)1;
	  }
	  else {
	    prob = ((int_4)1 - sum) / num;
	    
	    // check if sum of probabilities of all outgoing arcs < 1
	    //
	    if (prob < (float_4)(0)) {
	      error_handler_cc((char_1*)"lattice_write_cc",
			       (char_1*)"Error, sum of probabilities of all outgoing arcs greater than one\n");
	    }
	  }
	  sum = (float_4)1;
	}
	
	// loop over all the next nodes to replace the zero probabilities with
	// the equally probable probability
	//
	count = (int_4)0;
	for (Link_node* lnd = nodes_list->get_head_cc();
	     lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	  
	  if (lm_scores[count] == (float_4)0) {
	    start_latn->replace_lm_score_cc(prob, count);
	  }
	  
	  // increment count
	  //
	  count++;
	}
	
	// loop over again all the next nodes to get the sum of probalitities
	// of all the words emanating form this node
	//
	if((probablistic_flag_a) || (allprob_flag_a)) {
	  count = (int_4)0;
	  num = (int_4)0;
	  sum = (float_4)0;			
	  for (Link_node* lnd = nodes_list->get_head_cc();
	       lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	    
	    // sum of probabilities of all outgoing arcs
	    //
	    sum += lm_scores[count];
	    
	    // increment count
	    //
	    count++;
	  }
	}
	
	// check if sum of probabilities of all outgoing arcs < 1
	//
	if (sum < (float_4)(1 - __ISIP_PROB_PRECISION)) {
	  error_handler_cc((char_1*)"lattice_write_cc",
			   (char_1*)"Error, sum of probabilities of all outgoing arcs less than one\n");
	}
	
	// check if sum of probabilities of all outgoing arcs > 1
	//
	if (sum > (float_4)(1 + __ISIP_PROB_PRECISION)) {
	  error_handler_cc((char_1*)"lattice_write_cc",
			   (char_1*)"Error, sum of probabilities of all outgoing arcs greater than one\n");
	}
	
	// check count
	//
	if (count != num_arcs_out) {
	  error_handler_cc((char_1*)"lattice_write_cc",
			   (char_1*)"Mismatch in number of outgoing arcs.");
	}
	
	// get updated lm scores
	//
	lm_scoresup = start_latn->get_lm_scores_cc();
	
      }
      
      // loop over all the next nodes again to print the arc information
      //
      count = (int_4)0;
      for (Link_node* lnd = nodes_list->get_head_cc();
	   lnd != (Link_node*)NULL; lnd = lnd->get_next_cc()) {
	
	// get the end lattice node for each arc
	//
	end_latn = (Lattice_node*)(lnd->get_item_cc());

	// print the arc information and update the arc count
	//
	fprintf(fp_lat_a, "J=%-8ld ", arc_count++);
	fprintf(fp_lat_a, "S=%-8ld ", i);
	fprintf(fp_lat_a, "E=%-8ld ", end_latn->get_node_index_cc());
	fprintf(fp_lat_a, "W=%-16s ", end_latn->get_word_cc()->get_name_cc());
	fprintf(fp_lat_a, "v=%-2ld ", end_latn->get_pron_var_cc());
	fprintf(fp_lat_a, "a=%-10.2f ", ac_scores[count]);

	if (probflag_a) {
	fprintf(fp_lat_a, "l=%-3.3f\n", log(lm_scoresup[count]));
	}
	else {
	  fprintf(fp_lat_a, "l=%-3.3f\n", lm_scores[count]);
	}
	  
	// increment count
	//
	count++;
      }
      
    } // end if nodes list is not null
  } // end for i loop
  
  // free memory
  //
  if (lnodes != (Lattice_node**)NULL) {
    delete [] lnodes;
    lnodes = (Lattice_node**)NULL;
  }
  
  // exit gracefully
  //
  return ISIP_TRUE;
}
