// file: $isip/class/pr/LanguageModelJSGF/lmjsgf_07.cc
//

// system include files
//
#include <typeinfo>

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

// method: digraphToJSGF
//
// arguments:
//   DiGraph<SearchNode> graph_a: (input) a DiGraph object to be converted
//   SearchSymbol grammar_name_a: (input) the JSGF grammar name for the graph
//
// return: (output) a JSGF grammar string converted from DiGraph
//
// This method converts the input grammar from NATIVE format
// to JSGF format. Each SearchSymbol is picked up from the input symbol table
// according to the SearchNode index in the graph.
// 
String LanguageModelJSGF::digraphToJSGF(DiGraph<SearchNode>& graph_a,
				    SearchSymbol grammar_name_a) const {
  
  // declare and initialize a JSGF grammar string
  //
  String JSGF_output(COMMENT_GRAMMAR_NAME);
  JSGF_output.concat(L"\n"); 
  JSGF_output.concat(L"  grammar network.grammar.");
  JSGF_output.concat(grammar_name_a);
  JSGF_output.concat(L";\n\n");
  JSGF_output.concat(L"  // Define the rules\n");
  JSGF_output.concat(L"  public");

  String debug_string;

  // get all vertices in the input graph
  //
  long num_vertices = graph_a.length() + 2;
  GraphVertex<SearchNode>* verts[num_vertices];

  // set the first vertex
  //
  verts[0] = graph_a.getStart();
  
  graph_a.gotoFirst();
  for(long i = 1; i < num_vertices - 1; i++) {
    verts[i] = (GraphVertex<SearchNode>*)graph_a.getCurr();
    graph_a.gotoNext();
  }

  // set the last vertex
  //
  verts[num_vertices - 1] = graph_a.getTerm();

  // set the rules names
  //
  String rule_names[num_vertices];

  // init the initial rule names
  //
  rule_names[0].assign(PARAM_JSGF_START_SYMBOL);
  rule_names[num_vertices - 1].assign(PARAM_JSGF_TERM_SYMBOL);

  for (int i = 1; i < num_vertices -1 ; i++){
    rule_names[i].assign(L"extention");
    rule_names[i].concat((long)i);
  }

  // set the rule bodies
  //
  String rule_bodies[num_vertices];
  GraphVertex<SearchNode>* curr_vert = (GraphVertex<SearchNode>*) NULL;
  
  for (int i = 0; i < num_vertices; i++){

    // get current vertex
    //
    curr_vert = verts[i];

    // set the symbol name
    //
    SearchSymbol symbol_name;
    if (verts[i]->isStart()){
      symbol_name.assign(L"<");
      symbol_name.concat(PARAM_JSGF_START_SYMBOL);
      symbol_name.concat(L">");
    }
    else if (verts[i]->isTerm()){
      symbol_name.assign(L"<");
      symbol_name.concat(PARAM_JSGF_TERM_SYMBOL);      
      symbol_name.concat(L">");
    }
    else{
      verts[i]->getItem()->getSymbol(symbol_name);
    }
    
    rule_bodies[i].assign(symbol_name);
    
    // seperate the symbol name from
    // following tokens
    //
    rule_bodies[i].concat(L" ");

    String vertices_rule;
    
    int flag = 0;
    
    // loop through all the available arcs starting from the current vertex
    //
    for (boolean more_paths = curr_vert->gotoFirst(); more_paths;
	 more_paths = curr_vert->gotoNext()) {

      // get current arc
      //
      GraphArc<SearchNode>* curr_arc = curr_vert->getCurr();
      float weight = curr_arc->getWeight();
      GraphVertex<SearchNode>* tmp_vert = curr_arc->getVertex();

      int index = -1;
      for (index = 0; index < num_vertices; index++){
	if ( verts[index] == tmp_vert ){
	  break;
	}
      }

      String rule_name = rule_names[index];
      Console::put(rule_name);
      
      // write the rule
      //
      // if it is the first one
      //
      if ( flag == 0){

	vertices_rule.concat(L"( /");
      }
      else {
	vertices_rule.concat(L"| ( /");	
      }
      
      vertices_rule.concat(weight);
      vertices_rule.concat(L"/ <");
      vertices_rule.concat(rule_name);	
      vertices_rule.concat(L"> )");
      flag++;
    }

    // group all next vertices
    //
    if ( flag > 1 ){
      String tmp_body(L"(");
      tmp_body.concat(vertices_rule);
      tmp_body.concat(L")");
      rule_bodies[i].concat(tmp_body);
    }
    else {
      rule_bodies[i].concat(vertices_rule);
    }
    
  }

  // write the whole rule
  //
  for (int i = 0; i < num_vertices; i++){

    // set the rule name
    //
    String rule_name;
    
    if (verts[i]->isStart()){
      rule_name.assign(grammar_name_a);
    }
    else if (verts[i]->isTerm()){
      continue;
    }
    else{
      rule_name = rule_names[i];
    }
    
    JSGF_output.concat(L"  ");
    JSGF_output.concat(L"<");
    JSGF_output.concat(rule_name);
    JSGF_output.concat(L"> = ");
    JSGF_output.concat(rule_bodies[i]);
    JSGF_output.concat(L" ;\n");
    
  }
  
  // return the JSGF grammar string
  //
  return JSGF_output;
}

// method: alignGraphs
//
// arguments:
//    Vector< Digraph<String> >& graph_list_a: (output) vector of graphs
//    Vector<SearchSymbol>& symbol_table_a: (input) symbol table
//    Vector<String>& graph_name_list_a: (input) vector of grammar names
//
// return: a boolean value indicating status
//
// this method change the order of the graphs in given vector to
// match the order of search symbols in given symbol table
//
boolean LanguageModelJSGF::alignGraphs(Vector< Vector<JSGFToken> >& graph_list_a,
				   Vector<SearchSymbol>& symbol_table_a,
				   Vector<String>& graph_name_list_a) {

  // make sure the three input vectors have the same length
  //
  if( graph_list_a.length() != symbol_table_a.length() ||
      graph_list_a.length() != graph_name_list_a.length() ||
      graph_name_list_a.length() != symbol_table_a.length()) {

    symbol_table_a.debug(L"symbol_table");
    graph_name_list_a.debug(L"graph_name");

    return Error::handle(name(), L"cannot align graphs because the symbol table, graph list and graph name list don't have the same length", Error::TEST, __FILE__, __LINE__);
  }
  
  // declare a temp vector of graphs
  //
  Vector< Vector<JSGFToken> > tmp_graph_list;

  for(long i=0; i<symbol_table_a.length(); i++) {

    // get symbol
    //
    String symbol(symbol_table_a(i));
    //symbol.debug(L"symbol_table");

    for(long j=0; j<graph_name_list_a.length(); j++) {

      //graph_name_list_a(j).debug(L"graph_name");
      // search the matching graph and add it to the temp graph vector
      //
      if(symbol.eq(graph_name_list_a(j))) {
	tmp_graph_list.concat(graph_list_a(j));
	break;
      }
    } // end: for loop with j
  } // end: for loop with i

  // make sure the graph_list_a and the tmp_graph_list have the same length
  //
  if(graph_list_a.length() != tmp_graph_list.length()) {
    
    return Error::handle(name(), L"invalid alignment of graphs", Error::TEST, __FILE__, __LINE__);
  }
  
  // update the graph_list_a
  //
  graph_list_a.assign(tmp_graph_list);

  // exit gracefully
  //
  return true;
}

// method: convertGraph
//
// arguments:
//   DiGraph<String>& graph_a: (input) a graph of seach symbols
//   DiGraph<SearchNode>& node_graph_a: (output) a graph of search nodes
//   long& num_symbols_a: (input) the number of symbols in the symbol table
//   SearchLevel& level_a: (input) search level to hold the graph
//
// return: a boolean value indicating status 
//
// this method convert a given DiGraph<String> to a DiGraph<SearchNode>
//
boolean LanguageModelJSGF::convertGraph(DiGraph<String>& symbol_graph_a,
				    DiGraph<SearchNode>& node_graph_a,
				    long& num_symbols_a,
				    long level_a) {

  // variable for graphs conversion
  //
  long symbol_index = -1;
  long num_vertices = symbol_graph_a.length() + 2;
  Vector<SearchNode> snode(num_vertices);
  GraphVertex<SearchNode>* snode_vert[num_vertices];
  GraphVertex<String>* tmp_vert[num_vertices];
  
  // index all the vertices so that they can be referenced easily
  //
  tmp_vert[0] = symbol_graph_a.getStart();

  // loop through the list of vertices and index them
  //
  symbol_graph_a.gotoFirst();

  for (long i = 1; i < num_vertices - 1; i++) {
    tmp_vert[i] = (GraphVertex<String>*)symbol_graph_a.getCurr();
    symbol_graph_a.gotoNext();
  }
  
  // the last vertex is the terminal node
  //
  tmp_vert[num_vertices - 1] = symbol_graph_a.getTerm();
    
  // create a list of SearchNode Vertices
  // and set the items properly
  //
  snode_vert[0] = node_graph_a.getStart();
  snode_vert[num_vertices - 1] = node_graph_a.getTerm();

  for (long i = 1; i < num_vertices - 1; i++) {
    // set the node in the given level
    //
    snode(i).setSearchLevel(&hg_d(level_a));

    // get the corresponding search symbol for the current node
    //
    String* current_symbol = tmp_vert[i]->getItem();
    
    // check if this symbol already exists in the level symbol table
    //
    for(long j=0; j<num_symbols_a; j++) {
  
      // get each symbol in the level
      //
      SearchSymbol existing_symbol;
      if(hg_d(level_a).isValidSymbol(j)) {
	hg_d(level_a).getSymbol(existing_symbol, j);
      }
      else {
	return Error::handle(existing_symbol,
			     L"invalid symbol in the search level symbol table",
			     Error::TEST,
			     __FILE__, __LINE__);
      }
      
      // compare two symbols
      //
      if(current_symbol->eq(existing_symbol)) {

	// set symbol index as the existing symbol in level
	//
	symbol_index = j;
	//set = true;
	break;
      }
    }

    // set the symbol id to the node
    //
    snode(i).setSymbolId(symbol_index);
  } // end: for (long i = 1; i < num_vertices - 1; i++)

  // add the vertices to the graph
  //
  for (long i = 1; i < num_vertices - 1; i++) {
    snode_vert[i] = node_graph_a.insertVertex(&snode(i));
  }

  // connect the vertices with arcs to complete the graph
  //
  for (long i = 0; i < num_vertices; i++) {
    // get the vertex
    //
    GraphVertex<String>* tmp_start_vert = tmp_vert[i];
    
    // get the list of arcs for this vertex
    //
    boolean arcs_remain = tmp_start_vert->gotoFirst();
    while (arcs_remain) {
      
      // declare temporary arcs and vertices
      //
      long dest_index = -1;
      GraphArc<String>* tmp_arc = tmp_start_vert->getCurr();
      GraphVertex<String>* dest_vertex = tmp_arc->getVertex();
      boolean is_eps = tmp_arc->getEpsilon();
      
      // find the index of the destination vertex
      //
      for (long j = 0; j < num_vertices; j++) {
	if (dest_vertex == tmp_vert[j]) {
	  dest_index = j;
	  break;
	} 
      }
      
      // connect the vertices in the searchnode graph
      //
      node_graph_a.insertArc(snode_vert[i], snode_vert[dest_index],
			   is_eps,
			   (float)tmp_arc->getWeight());
      
      // goto the next arc
      //
      arcs_remain = tmp_start_vert->gotoNext();
    }
  }
  // gracefully exit
  //
  return true;
}
