// file: $isip/class/stat/SearchLevel/slev_03.cc
// version: $Id: slev_03.cc 10570 2006-05-13 23:51:06Z may $
//

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

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

  // read the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;  
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size in bytes of object (or full_size)
//  boolean param: (input) is the parameter name in the file?
//  boolean nested: (input) are we nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
boolean SearchLevel::readData(Sof& sof_a, const String& pname_a,
			      long size_a, boolean param_a,
			      boolean nested_a) {

  Integral::DEBUG debug_level = Integral::NONE;
  SofParser parser;
  parser.setDebug(debug_level);
    
  // are we nested?
  //
  if (nested_a) {
    parser.setNest();
  }
  
  // load the parser
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }	    

  // read the level index
  //
  if (parser.isPresent(sof_a, PARAM_LEVEL_INDEX)) {
    if (!level_index_d.readData(sof_a, PARAM_LEVEL_INDEX,
				parser.getEntry(sof_a,
						PARAM_LEVEL_INDEX),
				true, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    level_index_d = DEF_LEVEL_INDEX;
  }

  // read the level tag
  //
  if (!readLevelTag(sof_a, parser)) {
    return false;
  }
  // must read the symbol table first
  //
  if (!readSymbols(sof_a, parser)) {
    return false;
  }

  // read the non-speech symbol table (optional)
  //
  readNonSpeechSymbols(sof_a, parser);

  // read the dummy symbol table (optional)
  //
  readDummySymbols(sof_a, parser);

  // read the exclude symbol table (optional)
  //
  readExcludeSymbols(sof_a, parser);

  // read the nsymbol exclude symbol table (optional)
  //
  readNSymbolExcludeSymbols(sof_a, parser);

  // read the spenalty exclude symbol table (optional)
  //
  readSPenaltyExcludeSymbols(sof_a, parser);    
  
  // read the context less symbol table (optional)
  //
  readContextLessSymbols(sof_a, parser);

  // read the skip symbol table (optional)
  //
  readSkipSymbols(sof_a, parser);  

  // read the non adapt symbol table (optional)
  //
  readNonAdaptSymbols(sof_a, parser);
  
  // read the sub graphs
  //
  if (!readSubGraphs(sof_a, parser)) {
    return false;
  }

  // read the context mapping table (optional)
  //
  readContextMapping(sof_a, parser);
  
  
  // exit gracefully
  //
  return true;
}

// method: readContextLessSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the context less symbol table from Sof file
//
boolean SearchLevel::readContextLessSymbols(Sof& sof_a,
					    SofParser& parser_a) {
  
  // read the contextless symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_CONTEXTLESS_SYMBOL)) {
    if (!contextless_symbol_table_d.readData(sof_a,
					     PARAM_CONTEXTLESS_SYMBOL,
					     parser_a.getEntry(sof_a,
							       PARAM_CONTEXTLESS_SYMBOL),
					     false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
        // verify that the context less symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < contextless_symbol_table_d.length(); i++) {
      symbol.assign(contextless_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid context less symbol");
	return Error::handle(name(), L"readContextLessSymbols: there is a context less symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readSkipSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the context less symbol table from Sof file
//
boolean SearchLevel::readSkipSymbols(Sof& sof_a,
				     SofParser& parser_a) {
  
  // read the skip symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_SKIP_SYMBOL)) {
    if (!skip_symbol_table_d.readData(sof_a,
				      PARAM_SKIP_SYMBOL,
				      parser_a.getEntry(sof_a,
							PARAM_SKIP_SYMBOL),
				      false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the context less symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < skip_symbol_table_d.length(); i++) {
      symbol.assign(skip_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid context less symbol");
	return Error::handle(name(), L"readSkipSymbols: there is a context less symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readDummySymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the dummy symbol table from Sof file
//
boolean SearchLevel::readDummySymbols(Sof& sof_a, 
				      SofParser& parser_a) {  
  
  // read the dummy symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_DUMMY_SYMBOL)) {
    if (!dummy_symbol_table_d.readData(sof_a,
				       PARAM_DUMMY_SYMBOL,
				       parser_a.getEntry(sof_a,
							 PARAM_DUMMY_SYMBOL),
				       false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the dummy symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < dummy_symbol_table_d.length(); i++) {
      symbol.assign(dummy_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid dummy symbol");
	return Error::handle(name(), L"readDummySymbols: there is a dummy symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readNonAdaptSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the non adaptation symbol table from Sof file
//
boolean SearchLevel::readNonAdaptSymbols(Sof& sof_a, 
					 SofParser& parser_a) {
  
  // read the non adapt symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_NON_ADAPT_SYMBOL)) {
    if (!non_adapt_symbol_table_d.readData(sof_a,
					   PARAM_NON_ADAPT_SYMBOL,
					   parser_a.getEntry(sof_a,
							     PARAM_NON_ADAPT_SYMBOL),
					   false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the no adapt symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < non_adapt_symbol_table_d.length(); i++) {
      symbol.assign(non_adapt_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non adaptation symbol");
	return Error::handle(name(), L"readNonAdaptSymbols: there is a non adapt symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the exclude symbol table from Sof file
//
boolean SearchLevel::readExcludeSymbols(Sof& sof_a, 
					SofParser& parser_a) {
  
  // read the exclude symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_EXCLUDE_SYMBOL)) {
    if (!exclude_symbol_table_d.readData(sof_a,
					 PARAM_EXCLUDE_SYMBOL,
					 parser_a.getEntry(sof_a,
							   PARAM_EXCLUDE_SYMBOL),
					 false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the exclude symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < exclude_symbol_table_d.length(); i++) {
      symbol.assign(exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid exclude symbol");
	return Error::handle(name(), L"readExcludeSymbols: there is a exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readNSymbolExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the nsymbol exclude symbol table from Sof file
//
boolean SearchLevel::readNSymbolExcludeSymbols(Sof& sof_a, 
					       SofParser& parser_a) {

  // read the nsymbol exlcude symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_NSYMBOL_EXCLUDE_SYMBOL)) {
    if (!nsymbol_exclude_symbol_table_d.readData(sof_a,
						 PARAM_NSYMBOL_EXCLUDE_SYMBOL,
						 parser_a.getEntry(sof_a,
								   PARAM_NSYMBOL_EXCLUDE_SYMBOL),
						 false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the nsymbol exclude symbols are a subset of all valid symbols    //
    SearchSymbol symbol;
    for (int i = 0; i < nsymbol_exclude_symbol_table_d.length(); i++) {
      symbol.assign(nsymbol_exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid nsymbol exclude symbol");
	return Error::handle(name(), L"readNSymbolExcludeSymbols: there is a nsymbol exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readSPenaltyExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the spenalty exclude symbol table from Sof file
//
boolean SearchLevel::readSPenaltyExcludeSymbols(Sof& sof_a, 
						SofParser& parser_a) {

   // read the spenalty exclude symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_SPENALTY_EXCLUDE_SYMBOL)) {
    if (!spenalty_exclude_symbol_table_d.readData(sof_a,
						  PARAM_SPENALTY_EXCLUDE_SYMBOL,
						  parser_a.getEntry(sof_a,
								    PARAM_SPENALTY_EXCLUDE_SYMBOL),
						  false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    

    // verify that the spenalty exclude symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i = 0; i < spenalty_exclude_symbol_table_d.length(); i++) {
      symbol.assign(spenalty_exclude_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid spenalty exclude symbol");
	return Error::handle(name(), L"readExcludeSymbols: there is a spenalty exclude symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readLevelTag
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method reads the level tag
//
boolean SearchLevel::readLevelTag(Sof& sof_a,  SofParser& parser_a) {
  
  // read the level tag
  //
  if (parser_a.isPresent(sof_a, PARAM_LEVEL_TAG)) {
    if (!level_tag_d.readData(sof_a, PARAM_LEVEL_TAG,
			      parser_a.getEntry(sof_a,
						PARAM_LEVEL_TAG),
			      true, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    return Error::handle(name(), L"readData: level tag not found", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);	 
  }
  
  
  if (level_tag_d.length() == 0 ) {
    return Error::handle(name(), L"read - level tag is not specified",
			 Error::ARG, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;
}

// method: readSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the symbol table from Sof file
//
boolean SearchLevel::readSymbols(Sof& sof_a, 
				 SofParser& parser_a) {
				 
  // read the symbol table
  //
  if (parser_a.isPresent(sof_a, PARAM_SYMBOL)) {
    if (!symbol_table_d.readData(sof_a, PARAM_SYMBOL,
				 parser_a.getEntry(sof_a, PARAM_SYMBOL),
				 false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    return Error::handle(name(), L"readData: symbol table not found",
			 Error::READ,
			 __FILE__, __LINE__, Error::WARNING);	 
  }

  // read the nsymbol model if applicable
  //
  if (use_nsymbol_d) {
    
    // open the n-symbol model file
    //
    Sof tmp_file;
    if (!tmp_file.open(nsymbol_file_d)) {
      return Error::handle(name(), L"readSymbols", Error::ARG, __FILE__, __LINE__);
    }

    // read the n-symbol model
    //
    if (!loadNSymbolModel(tmp_file)) {
      return Error::handle(name(), L"readSymbols", Error::ARG, __FILE__, __LINE__);
    }
    tmp_file.close();
  }

  // exit gracefully
  //
  return true;
}

// method: readNonSpeechSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method read the non-speechsymbol table from Sof file
//
boolean SearchLevel::readNonSpeechSymbols(Sof& sof_a, SofParser& parser_a) {
  
  // read the nonspeech boundary symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_NONSPEECH_BOUNDARY_SYMBOL)) {
    if (!nonspeech_boundary_symbol_table_d.readData(sof_a,
						    PARAM_NONSPEECH_BOUNDARY_SYMBOL,
						    parser_a.getEntry(sof_a,
								      PARAM_NONSPEECH_BOUNDARY_SYMBOL),
						    false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    

    // verify that the non-speech symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i=0; i < nonspeech_boundary_symbol_table_d.length(); i++) {
      symbol.assign(nonspeech_boundary_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non-speech boundary symbol");
	return Error::handle(name(), L"readNonSpeechSymbols: there is a non-speech boundary symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }

  // read the nonspeech internal symbols
  //
  if (parser_a.isPresent(sof_a, PARAM_NONSPEECH_INTERNAL_SYMBOL)) {
    if (!nonspeech_internal_symbol_table_d.readData(sof_a,
						    PARAM_NONSPEECH_INTERNAL_SYMBOL,
						    parser_a.getEntry(sof_a,
								      PARAM_NONSPEECH_INTERNAL_SYMBOL),
						    false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
    
    // verify that the non-speech symbols are a subset of all valid symbols
    //
    SearchSymbol symbol;
    for (int i=0; i < nonspeech_internal_symbol_table_d.length(); i++) {
      symbol.assign(nonspeech_internal_symbol_table_d(i));
      if (!isValidSymbol(getSymbolIndex(symbol))) {
	symbol.debug(L"invalid non-speech internal symbol");
	return Error::handle(name(), L"readNonSpeechSymbols: there is a non-speech internal symbol that does not appear in the symbol table", SearchLevel::ERR, __FILE__, __LINE__);	
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readSubGraphs
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean indicating status
//
// this method has the object read itself from an Sof file
//
boolean SearchLevel::readSubGraphs(Sof& sof_a, 
				   SofParser& parser_a) {

  // make sure the symbol table has been read
  //
  long num_symbols = symbol_table_d.length();
  
  if (num_symbols == 0) {
    return Error::handle(name(), L"readSubGraphs - symbol table has not been read", SearchLevel::ERR, __FILE__, __LINE__);
  }

  // read the sub graphs
  //
  if (parser_a.isPresent(sof_a, PARAM_MODEL)) {
    if (!tmp_graphs_d.readData(sof_a, PARAM_MODEL,
			       parser_a.getEntry(sof_a, PARAM_MODEL),
			       false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    return false;
  }

  return convertDigraphs();

}

// method: readContextMapping
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//
// return: a boolean value indicating status
//
// this method reads context mapping information from an Sof file
//
boolean SearchLevel::readContextMapping(Sof& sof_a,
					SofParser& parser_a) {
  
  // make sure the symbol table has been read
  //
  long num_symbols = symbol_table_d.length();  
  if (num_symbols < 1) {
    return Error::handle(name(), L"readContextMapping", Error::ARG, __FILE__, __LINE__);
  }
  
  // read the context mapping table
  //
  if (parser_a.isPresent(sof_a, PARAM_CONTEXT_MAPPING)) {
    if (!context_map_d.readData(sof_a, PARAM_CONTEXT_MAPPING,
				parser_a.getEntry(sof_a, PARAM_CONTEXT_MAPPING),
				false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }

  // when there is a context mapping table at this level
  //
  if (context_map_d.length() > 0) {
  
    // add start and terminal search symbol to the symbol table
    //
    symbol_table_d.concat(SearchSymbol::NO_LEFT_CONTEXT);
    symbol_table_d.concat(SearchSymbol::NO_RIGHT_CONTEXT);
    
    // set up start and terminal search node for each subgraph
    //
    for (long i = 0; i < sub_graphs_d.length(); i++) {
      
      // start vertex
      //
      SearchNode* snode_p = new SearchNode();
      snode_p->setSearchLevel(this);
      snode_p->setSymbol(SearchSymbol::NO_LEFT_CONTEXT);
      sub_graphs_d(i).getStart()->setItem(snode_p);
      
      // terminal vertex
      //
      snode_p = new SearchNode();
      snode_p->setSearchLevel(this);
      snode_p->setSymbol(SearchSymbol::NO_RIGHT_CONTEXT);
      sub_graphs_d(i).getTerm()->setItem(snode_p);
    }
    
    // insert all context pairs into the context mapping hash table
    //
    long total_context_length = left_context_d + right_context_d + 1;

    // check the context length
    //
    if (context_map_d.length() > 0 ) {
      if (context_map_d(0).getContext().length() != total_context_length) {
	total_context_length = context_map_d(0).getContext().length();
      }
    }
    
    Context context(total_context_length);
    
    // loop over all context pairs
    //
    for (long i = 0; i < context_map_d.length(); i++) {
      
      // loop over the symbols in the context
      //
      for (long j = 0; j < total_context_length; j++) {
	
	SearchSymbol ss(context_map_d(i).getContext()(j));
	long symbol_id = getSymbolIndex(ss);
	
	// check whether the symbol is valid
	//
	if (symbol_id == -1) {
	  ss.debug(L"symbol");
	  return Error::handle(name(), L"readContextMapping", Error::ARG, __FILE__, __LINE__);
	}
	else {
	  context.assignAndAdvance(symbol_id);
	}
      }
      
      // insert a context specification into the hash table
      //
      Ulong index = context_map_d(i).getContextIndex();

      // check if the context is already in the table
      //
      Ulong* existing_index = context_hash_d.get(context);

      // if this context is not in the table yet, insert it
      //
      if (existing_index == NULL) {
	context_hash_d.insert(context, &index);
      }
      
      // if the context is already in the table, then check whether
      // the index of the model is the same as the one from table
      //
      else {

	// if indices are different, explain conflict and return error
	//
	if (!existing_index->eq(index)) {
	  context.debug(L"Context:");
	  String out;
	  out.concat(L" is already in context mapping table with the index: ");
	  out.concat(*existing_index);
	  out.concat(L"\n while new index is: ");
	  out.concat(index);
	  Console::put(out);
	  return Error::handle(name(), L"readContextMapping", Error::ARG,
			       __FILE__, __LINE__);
	}
	
	// otherwise indices are not conflicting, just print a warning
	//
	else {
	  context.debug(L"Warning: This context is already in the table:");
	}
      }
    }
    
    // output the debugging information
    //
    if (debug_level_d >= Integral::ALL) {     
      context_hash_d.debug(L"context hash table:");
    }
  }
  
  // when there is NO context mapping table at this level
  //
  else {
    
    // symbols will be mapped to the model with the same index at lower level
    //
    Context dummy(1);
    
    for (Ulong i = 0; i < (ulong)num_symbols; i++) {
      dummy.assignAndAdvance(i);
      context_hash_d.insert(dummy, &i);
    }

    if (debug_level_d >= Integral::ALL) {
      context_hash_d.debug(L"dummy context hash table:");
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: readConfig
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method reads configuration parameters from a file
//
boolean SearchLevel::readConfig(Sof& sof_a, long tag_a, const String& name_a) {

  // read the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  Integral::DEBUG debug_level = Integral::NONE;
  SofParser parser;
  parser.setDebug(debug_level);
    
  // load the parser
  //
  if (!parser.load(sof_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }	    

  // read the acoustic rescoring parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_ACOUSTIC_SCORE)) {
    if (!use_acoustic_score_d.readData(sof_a, PARAM_USE_ACOUSTIC_SCORE,
				       parser.getEntry(sof_a,
						PARAM_USE_ACOUSTIC_SCORE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    use_acoustic_score_d = DEF_USE_ACOUSTIC_SCORE;
  }

  // read the acoustic rescoring parameters
  //
  if (parser.isPresent(sof_a, PARAM_ACOUSTIC_SCALE)) {
    if (!acoustic_scale_d.readData(sof_a, PARAM_ACOUSTIC_SCALE,
				     parser.getEntry(sof_a,
						PARAM_ACOUSTIC_SCALE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    acoustic_scale_d = DEF_ACOUSTIC_SCALE;
  }  
  
  // read the symbol graph generation parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_SYMBOL_GRAPH)) {
    if (!use_symbol_graph_d.readData(sof_a, PARAM_USE_SYMBOL_GRAPH,
				parser.getEntry(sof_a,
						PARAM_USE_SYMBOL_GRAPH))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    use_symbol_graph_d = DEF_USE_SYMBOL_GRAPH;
  }

  if (parser.isPresent(sof_a, PARAM_HISTORY_PATHS)) {
    if (!history_paths_d.readData(sof_a, PARAM_HISTORY_PATHS,
				  parser.getEntry(sof_a,
						  PARAM_HISTORY_PATHS))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    history_paths_d = DEF_HISTORY_PATHS;
  }  
  
  // read the nsymbol parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_NSYMBOL)) {
    if (!use_nsymbol_d.readData(sof_a, PARAM_USE_NSYMBOL,
				parser.getEntry(sof_a,
						PARAM_USE_NSYMBOL))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    use_nsymbol_d = DEF_USE_NSYMBOL;
  }
  
  if (parser.isPresent(sof_a, PARAM_NSYMBOL_ORDER)) {
    if (!nsymbol_order_d.readData(sof_a, PARAM_NSYMBOL_ORDER,
		     parser.getEntry(sof_a,
				     PARAM_NSYMBOL_ORDER))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    nsymbol_order_d = DEF_NSYMBOL_ORDER;
  }  

  if (parser.isPresent(sof_a, PARAM_NSYMBOL_MODEL)) {
    if (!nsymbol_file_d.readData(sof_a, PARAM_NSYMBOL_MODEL,
			   parser.getEntry(sof_a,
					   PARAM_NSYMBOL_MODEL))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }

  // read the scale and symbol insertion parameters
  //
  if (parser.isPresent(sof_a, PARAM_LM_SCALE)) {
    if (!lm_scale_d.readData(sof_a, PARAM_LM_SCALE,
		     parser.getEntry(sof_a,
				     PARAM_LM_SCALE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    lm_scale_d = DEF_LM_SCALE;
  }
  
  if (parser.isPresent(sof_a, PARAM_TR_SCALE)) {
    if (!tr_scale_d.readData(sof_a, PARAM_TR_SCALE,
		     parser.getEntry(sof_a,
				     PARAM_TR_SCALE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    tr_scale_d = DEF_TR_SCALE;
  }
  
  if (parser.isPresent(sof_a, PARAM_SYMBOL_PENALTY)) {
    if (!symbol_penalty_d.readData(sof_a, PARAM_SYMBOL_PENALTY,
			   parser.getEntry(sof_a,
					   PARAM_SYMBOL_PENALTY))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    symbol_penalty_d = DEF_SYMBOL_PENALTY;
  }

  // read the context parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_CONTEXT)) {
    if (!use_context_d.readData(sof_a, PARAM_USE_CONTEXT,
			   parser.getEntry(sof_a,
					   PARAM_USE_CONTEXT))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    use_context_d = DEF_USE_CONTEXT;
  }

  if (parser.isPresent(sof_a, PARAM_LEFT_CONTEXT_LENGTH)) {
    if (!left_context_d.readData(sof_a, PARAM_LEFT_CONTEXT_LENGTH,
				 parser.getEntry(sof_a,
				 PARAM_LEFT_CONTEXT_LENGTH))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    left_context_d = DEF_LEFT_CONTEXT;
  }

  if (parser.isPresent(sof_a, PARAM_RIGHT_CONTEXT_LENGTH)) {
    if (!right_context_d.readData(sof_a, PARAM_RIGHT_CONTEXT_LENGTH,
				 parser.getEntry(sof_a,
				 PARAM_RIGHT_CONTEXT_LENGTH))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    left_context_d = DEF_RIGHT_CONTEXT;
  }    
  
  // read the lexical tree
  //
  if (parser.isPresent(sof_a, PARAM_USE_LEXICAL_TREE)) {
    if (!use_lexical_tree_d.readData(sof_a, PARAM_USE_LEXICAL_TREE,
			   parser.getEntry(sof_a,
					   PARAM_USE_LEXICAL_TREE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    use_lexical_tree_d = DEF_USE_LEXICAL_TREE;
  }

  // read the beam prunning parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_BEAM)) {
    if (!use_beam_prune_d.readData(sof_a, PARAM_USE_BEAM,
				   parser.getEntry(sof_a,
						   PARAM_USE_BEAM))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }  
  else {
    use_beam_prune_d = DEF_BEAM_PRUNE;
  }

  if (parser.isPresent(sof_a, PARAM_BEAM_THRESHOLD)) {
    if (!beam_threshold_d.readData(sof_a, PARAM_BEAM_THRESHOLD,
				   parser.getEntry(sof_a,
						   PARAM_BEAM_THRESHOLD))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }  
  else {
    beam_threshold_d = DEF_BEAM_THRESHOLD;
  }    
  
  // read the instance prunning parameters
  //
  if (parser.isPresent(sof_a, PARAM_USE_INSTANCE)) {
    if (!use_instance_prune_d.readData(sof_a, PARAM_USE_INSTANCE,
				  parser.getEntry(sof_a,
						  PARAM_USE_INSTANCE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }  
  else {
    use_instance_prune_d = DEF_INSTANCE_PRUNE;
  }
  
  if (parser.isPresent(sof_a, PARAM_INSTANCE_THRESHOLD)) {
    if (!instance_threshold_d.readData(sof_a, PARAM_INSTANCE_THRESHOLD,
				   parser.getEntry(sof_a,
						   PARAM_INSTANCE_THRESHOLD))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }  
  else {
    instance_threshold_d = DEF_INSTANCE_THRESHOLD;
  }
  
  // read the symbol occupancies parameters
  //
  if (parser.isPresent(sof_a, PARAM_WRITE_SYMBOL_OCCUPANCY)) {
    if (!write_symbol_occupancy_d.readData(sof_a, PARAM_WRITE_SYMBOL_OCCUPANCY,
					   parser.getEntry(sof_a,
					      PARAM_WRITE_SYMBOL_OCCUPANCY))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  else {
    write_symbol_occupancy_d = DEF_WRITE_SYMBOL_OCCUPANCY;
  }  

  if (parser.isPresent(sof_a, PARAM_SYMBOL_OCCUPANCY_FILE)) {
    if (!symbol_occupancy_file_d.readData(sof_a, PARAM_SYMBOL_OCCUPANCY_FILE,
					  parser.getEntry(sof_a,
					       PARAM_SYMBOL_OCCUPANCY_FILE))) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }    
  }
  
  
  if (parser.isPresent(sof_a, PARAM_NONSPEECH_INTERNAL_SYMBOL)) {
    if (!readDataNonSpeechInternalSymbol(parser, sof_a)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);      
    }
	}  
    
  // exit gracefully
  //
  return true;  
}

// method: parseSymbols
//
// arguments:
//  String& symbols_string: (input) string containing search symbols separated by ","
//  Vector<SearchSymbol>& symbols_vec: (output) Vector of search symbols parsed from input String
//
// return: a boolean indicating status
//
// this method parses a string containing search symbols separated by a ","
// and stores them in a vector of SearchSymbols.  This is used when a
// symbol type list must be defined in the configuration file
//
boolean SearchLevel::parseSymbols(String& symbols_string_a,
				  Vector<SearchSymbol>& symbols_vec_a) {

  // count the number of sub-strings separated by ','
  //
  long tokens = symbols_string_a.countTokens(L',');

  // string that will temporarily hold the current parsed symbol
  //
  String symbol_str;

  // set the length of the SearchSymbol vector to the number of symbols in string
  //
  symbols_vec_a.setLength(tokens);

  // add all of symbols parsed from string to the SearchSymbol vector
  //
  for (long i = 0, pos = 0; i < tokens; i++) {

    symbols_string_a.tokenize(symbol_str, pos, L",");

    // trim leading and trailing space
    //
    symbol_str.trim();

    symbols_vec_a(i) = symbol_str;
  }

  // exit gracefully
  //
  return true;
}

// method: readDataNonSpeechInternalSymbol
//
// arguments:
//  SofParser& parser: (input) parser
//  Sof& sof: (input) sof file
//
// return: a boolean indicating status
//
// this method reads the nonspeech_internal_symbol as a string parameter from
// the configuration file
//
boolean SearchLevel::readDataNonSpeechInternalSymbol(SofParser& parser_a,
						     Sof& sof_a) {
  
  
  // string that will contain the nonspeech_internal_symbols separated by a ','
  //
  String nonspeech_internal_symbol_str;
  
  
  // only read this parameter if the vector size is 0
  //
  if (nonspeech_internal_symbol_table_d.length() == (long)0) {
    
    // parse the configuration file for this parameter and read it
    // into a string
    //
    if (!nonspeech_internal_symbol_str.readData(sof_a,
						PARAM_NONSPEECH_INTERNAL_SYMBOL,
						parser_a.getEntry(sof_a,
								  PARAM_NONSPEECH_INTERNAL_SYMBOL))) {
      return Error::handle(name(), L"readDataNonSpeechInternalSymbol",
			   Error::READ, __FILE__, __LINE__, Error::WARNING);
    }
    
    // parse the string containing the nonpeech_internal_sumbols
    //
    parseSymbols(nonspeech_internal_symbol_str,
		 nonspeech_internal_symbol_table_d);
  }
  
  // exit gracefully
  //
  return true;
}
