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

// there is no source code in this file
//

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

// method: write
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: boolean value indicating status
//
// this method has the object write itself to an Sof file
//
boolean LanguageModelJSGF::write(Sof& sof_a, long tag_a,
			     const String& name_a) const {

  long obj_size = 0;
  
  // write the instance of the object into the Sof file
  //
  if (sof_a.isText()) {

    // set the size to by dynamic
    //
    obj_size = Sof::ANY_SIZE;
  }
  else {

    // the size index and the size of each element
    //
    obj_size = sofSize();
  }

  // write the object into the sof file's index
  //
  if (!sof_a.put(name_a, tag_a, obj_size)) {
    return false;
  }
  
  // exit gracefully
  //
  return writeData(sof_a);
}

// method: writeData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//
// return: boolean value indicating status
//
// this method has the object write itself to an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
boolean LanguageModelJSGF::writeData(Sof& sof_a, const String& pname_a) const {
  
  // write a start string if necessary
  //
  sof_a.writeLabelPrefix(pname_a);

  HierarchicalDigraph h_digraph(rm_d.second());

  Vector<SearchSymbol> temp_vector;
  
  // write the start symbol
  //
  temp_vector.concat(START_SYMBOL);
  if (!writeSymbolType(sof_a, DEF_LEVEL, PARAM_JSGF_START_SYMBOL,
		       temp_vector)) {
    return Error::handle(name(), L"error writing start symbol", Error::WRITE,
			 __FILE__, __LINE__);
  }
  temp_vector.clear();      

  // write the term symbol
  //
  temp_vector.concat(TERM_SYMBOL);
  if (!writeSymbolType(sof_a, DEF_LEVEL, PARAM_JSGF_TERM_SYMBOL,
		       temp_vector)) {
    return Error::handle(name(), L"error writing term symbol", Error::WRITE,
			 __FILE__, __LINE__);
  }
  temp_vector.clear();

  // loop over each level
  //
  for (long level = 0; level < h_digraph.length(); level++) {


    // write the level tag
    //
    temp_vector.concat(h_digraph(level).getLevelTag());
    if (!writeSymbolType(sof_a, level, SearchLevel::PARAM_LEVEL_TAG,
			 temp_vector)) {
      return Error::handle(name(), L"error writing level tag", Error::WRITE,
			   __FILE__, __LINE__);
    }
    temp_vector.clear();

    // write the grammars for this level
    //
    if (!writeGrammars(sof_a, level, h_digraph)) {
      return Error::handle(name(), L"error writing grammars", Error::WRITE,
			   __FILE__, __LINE__);      
    }
    
    // write the nonspeech boundary symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_NONSPEECH_BOUNDARY_SYMBOL,
		    h_digraph(level).getNonSpeechBoundarySymbolTable());

    // write the nonspeech internal symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_NONSPEECH_INTERNAL_SYMBOL,
		    h_digraph(level).getNonSpeechInternalSymbolTable());

    // write the dummy symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_DUMMY_SYMBOL,
		    h_digraph(level).getDummySymbolTable());

    // write the exclude symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_EXCLUDE_SYMBOL,
		    h_digraph(level).getExcludeSymbolTable());

    // write the nsymbol exclude symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_NSYMBOL_EXCLUDE_SYMBOL,
		    h_digraph(level).getNSymbolExcludeSymbolTable());

    // write the spenalty exclude symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_SPENALTY_EXCLUDE_SYMBOL,
		    h_digraph(level).getSPenaltyExcludeSymbolTable());

    // write the context less symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_CONTEXTLESS_SYMBOL,
		    h_digraph(level).getContextLessSymbolTable());

    // write the skip symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_SKIP_SYMBOL,
		    h_digraph(level).getSkipSymbolTable());

    // write the non adaptation symbols
    //
    writeSymbolType(sof_a, level, SearchLevel::PARAM_NON_ADAPT_SYMBOL,
		    h_digraph(level).getNonAdaptSymbolTable());

    // write the context mapping (if it exists) for this level
    //
    if (h_digraph(level).getContextMap().length() > 0) {
      if (!writeContextMapping(sof_a, level, h_digraph)) {
	return Error::handle(name(), L"error writing context mapping",
			     Error::WRITE, __FILE__, __LINE__);
      }
    }	
  }
  
  // put an end string if necessary
  //
  sof_a.writeLabelSuffix(pname_a);
  
  // exit gracefully
  //
  return true;

}

// method: writeGrammars
//
// arguments:
//  Sof& sof: (input) sof file object
//  long leve: the current level
//
// return: boolean value indicating status
//
// this method writes the grammar for a particular level to the
// language model file
//
boolean LanguageModelJSGF::writeGrammars(Sof& sof_a, long level_a,
					 HierarchicalDigraph& h_digraph_a) const {

  Vector<String> grammars;

  Vector<SearchSymbol> symbols;

  String param_name(PARAM_GRAMMARS);
  param_name.concat(PARAM_UNDERSCORE);
  param_name.concat((Long)level_a);
  
  long num_graphs = grammars_d(level_a).length();

  // find out if we had context mapping in the previous level.
  // if so, we need to create a new set of symbols corresponding
  // to each of the contexts.  these symbols will be used to label
  // the subgraphs for each context.  if there is no context in
  // the previous level, we'll use the regular search symbols
  //
  if (level_a > 0) {
    if (h_digraph_a(level_a - 1).getContextMap().length() > 0) {
      for (long j = 0; j < num_graphs; j++) {
	String symbol;
	symbol.assign(CONTEXT_LABEL_PREFIX);
	Ulong index;
	index.assign(j);
	symbol.concat(index);
	symbols.concat(symbol);
      }
    }
    else {
      symbols.assign(h_digraph_a(level_a-1).getSymbolTable());
    }
  }
  // if this is the top level (level 0), the name of the graph is,
  // by default, TOP_GRAMMAR_NAME
  else {
    symbols.concat(TOP_GRAMMAR_NAME);
  }

  // loop through all of the graphs, create the grammar string, and
  // add the grammar string to the grammars vector
  //
  for (int i = 0; i < num_graphs; i++) {

    // declare a JSGF grammar string
    //
    //    String jsgf_grammar(PARAM_JSGF_VERSION);
    //jsgf_grammar.concat(L"\n  ");    

    String jsgf_grammar;
    
    // get the graph
    //
    //DiGraph<SearchNode>& graph = h_digraph_a(level_a).getSubGraph(i);

    // convert graph JSGF
    //
    //    jsgf_grammar.concat(digraphToJSGF(graph, symbols(i)));

    convertTokensToString(grammars_d(level_a)(i), jsgf_grammar);

    // add this grammar to the vector of strings
    //
    grammars.concat(jsgf_grammar);
  }

  return grammars.writeData(sof_a, param_name);

}

// method: writeSymbolType
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean value indicating status
//
// this method read one rule from the sof file
//
boolean LanguageModelJSGF::writeSymbolType(Sof& sof_a, long level_a,
					   const String& pname_a,
					   Vector<SearchSymbol> symbol_list_a) const {

  // don't write if there are no symbols and we're in
  // text mode
  //
  if (symbol_list_a.length() == 0 && sof_a.isText()) {
    return false;
  }
  
  // resulting JSGF grammar
  //
  String grammar;

  // parameter name 
  //
  String param_name(pname_a);

  // set up the parameter name according to the level index
  //
  if (level_a != DEF_LEVEL) {
    param_name.concat(PARAM_UNDERSCORE);
    param_name.concat((Long)level_a);
  }
  
  // set up the JSGF grammar for particular symbol type
  //
  grammar.assign(L"\n  ");
  grammar.concat(PARAM_JSGF_VERSION);
  grammar.concat(L"\n  ");
  grammar.concat(COMMENT_GRAMMAR_NAME);
  grammar.concat(L"\n  grammar network.grammar.");

  // JSGFParser needs to be modified so that this isn't necessary.
  // without these conditional statements, parser will not read
  // start and term symbols correctly
  //
  String temp_name;
  if (pname_a.eq(PARAM_JSGF_START_SYMBOL)) {
    temp_name.assign(START_SYMBOL_REFERENCE);
  }
  else if (pname_a.eq(PARAM_JSGF_TERM_SYMBOL)) {
    temp_name.assign(TERM_SYMBOL_REFERENCE);
  }
  else {
    temp_name.assign(pname_a);
  }

  grammar.concat(temp_name);
  grammar.concat(L";\n\n  ");
  grammar.concat(COMMENT_SYMBOLS);
  grammar.concat(L"\n  public <");
  grammar.concat(temp_name);
  grammar.concat(L"> = ");
  
  for (int i = 0 ; i < symbol_list_a.length(); i++) {
    grammar.concat(symbol_list_a(i));
    grammar.concat(L" ");
  }
  
  grammar.concat(L";\n");
  
  // write to the sof file
  //
  return grammar.writeData(sof_a, param_name);

}

// method: writeContextMapping
//
// arguments:
//    Sof& sof: (input) sof file object
//    const long tag: (input) sof object instance tag
//    SearchLevel& level: (input) search level
//    long level_index: (input) search level index
//
// return: a boolean value indicating status
//
// this method has the object store itself from an Sof file according
// to the specified name and tag
//
boolean LanguageModelJSGF::writeContextMapping(Sof& sof_a,
					       long level_a,
					       HierarchicalDigraph& h_digraph_a) const {
  
  // local variable
  //
  Vector<ContextMap> contexts;
  Vector<String> context_maps;
  String jsgf_prefix;
      
  // parameter name 
  //
  String param_name(SearchLevel::PARAM_CONTEXT_MAPPING);

  // set up the parameter name according to the level index
  //
  param_name.concat(PARAM_UNDERSCORE);
  param_name.concat((Long)level_a);

  // JSGF prefix for the grammar (common to all the contetmaps in JSGF
  // format)
  //
  jsgf_prefix.assign(PARAM_JSGF_VERSION);
  jsgf_prefix.concat(L"\n ");
  jsgf_prefix.concat(COMMENT_GRAMMAR_NAME);
  jsgf_prefix.concat(L"\n grammar network.grammar.context;\n\n ");
  jsgf_prefix.concat(COMMENT_CONTEXT_RULE);
  jsgf_prefix.concat(L"\n");
  
  // get the context maps from the searchlevel
  //
  contexts.assign(h_digraph_a(level_a).getContextMap());

  // set the length of the context-maps
  //
  long len = contexts.length();
  context_maps.setCapacity(len);
  context_maps.setLength(len);
  
  // loop over all the context_maps and parse string in each loop
  //
  for (long k = 0; k < contexts.length(); k++) {
    
    // local variable
    //
    String rule_name;
    String context_map;
    Vector<SearchSymbol> context;
    Ulong index;
    
    // get the context and the context-index from the current
    // context-map
    //
    context = contexts(k).getContext();
    index = contexts(k).getContextIndex();
    
    // set the rule name
    //
    rule_name.assign(L" public <");    
    for (long i = 0; i < context.length(); i++){
      
      // read the rule name
      //
      rule_name.concat(context(i));
      if ( i != (context.length() - 1)){
	rule_name.concat(L"-");
      }
    }
    
    rule_name.concat(L"> = ");
    rule_name.concat(CONTEXT_LABEL_PREFIX);
    rule_name.concat(index);
    rule_name.concat(L" ;");
    
    context_map.assign(jsgf_prefix);
    context_map.concat(rule_name);
    
    // add this JSGF contextmap to the output vector
    //
    context_maps(k).assign(context_map);
  }
  
  // write the vector of contextmaps to the sof file
  //
  context_maps.writeData(sof_a, param_name);
  
  // exit gracefully
  //
  return true;
}

// method: convertTokensToString
//
// arguments:
//  Vector<JSGFToken>: (input) a list of JSGF tokens 
//  String&: (output) a string containing the JSGF grammar
//           to be written to a file
//
// return: a boolean value indicating status
//
//
//
boolean LanguageModelJSGF::convertTokensToString(Vector<JSGFToken> grammar_a, String& string_grammar_a) const {

  
  String string_grammar;

  // loop over tokens for this graph and convert them to the
  // appropriate string.  this is a very simple process.
  //
  for (long i = 0; i < grammar_a.length(); i++) {

    if (grammar_a(i).getTokenType() == JSGFToken::HEADER) {
      string_grammar.concat(PARAM_JSGF_VERSION);
      string_grammar.concat(L"\n");      
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::KEYWORD) {
      string_grammar.concat(grammar_a(i).getKeyword());
      string_grammar.concat(L" ");
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::GRAMMAR_NAME) {
      string_grammar.concat(L" ");
      string_grammar.concat(KEYWORD_GRAMMAR);
      string_grammar.concat(L" ");
      string_grammar.concat(grammar_a(i).getGrammarName());

    }
    else if (grammar_a(i).getTokenType() == JSGFToken::IMPORT_GRAMMAR_NAME) {
      // not yet implemented
      //
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::RULE_NAME) {
      string_grammar.concat(grammar_a(i).operator_d(OPERATOR_OPEN_POS));
      string_grammar.concat(grammar_a(i).getRuleName());
      string_grammar.concat(grammar_a(i).operator_d(OPERATOR_CLOSE_POS));
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::TERMINAL) {
      
      string_grammar.concat(L" ");
	
      if (grammar_a(i).getTerminal().eq(START_SYMBOL)) {
	string_grammar.concat(OPERATOR_OPENANGLEBRACKET);
	string_grammar.concat(PARAM_JSGF_START_SYMBOL);
	string_grammar.concat(OPERATOR_CLOSEANGLEBRACKET);
      }
      else if (grammar_a(i).getTerminal().eq(TERM_SYMBOL)) {
	string_grammar.concat(PARAM_JSGF_TERM_SYMBOL);
      }
      else {
	string_grammar.concat(grammar_a(i).getTerminal());
      }
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::TAG) {
      // not yet implemented
      //
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::QUOTED_TOKEN) {
      // not yet implemented
      //
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::WEIGHT) {
      string_grammar.concat(L" ");	
      string_grammar.concat(grammar_a(i).operator_d(OPERATOR_OPEN_POS));
      string_grammar.concat(grammar_a(i).getWeight());
      string_grammar.concat(grammar_a(i).operator_d(OPERATOR_CLOSE_POS));
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::OPERATOR) {

      if (grammar_a(i).operator_d(OPERATOR_OPEN_POS).eq(L";")) {
	string_grammar.concat(grammar_a(i).operator_d(OPERATOR_OPEN_POS));
	string_grammar.concat(L"\n");
      }
      else {
	string_grammar.concat(L" ");
	string_grammar.concat(grammar_a(i).operator_d(OPERATOR_OPEN_POS));
      }
    }
    else if (grammar_a(i).getTokenType() == JSGFToken::MARKER) {
      // not yet implemented
      //
    }
  }

  string_grammar_a.assign(string_grammar);

  // exit gracefully
  //
  return true;
  
}
