// file: $isip/util/speech/isip_model_combiner/isip_model_combiner.cc
//
// isip include files
//
#include "isip_model_combiner.h"

// isip_model_combiner: converts a pool of statistical models into
//    the acoustic model format that can be used by the recognizer
//
int main(int argc, const char** argv) {

  // setup the command line
  //
  Sdb sdb;
  CommandLine cmdl(sdb);
  cmdl.setUsage(
#include "usage_message.text"
                );
  cmdl.setHelp(
#include "help_message.text"
               );

  cmdl.setIdent("$Revision: 10617 $",
		"$Name: $",
		"$Date: 2006-08-24 11:02:26 -0500 (Thu, 24 Aug 2006) $");
  
  // add a command line option for the parameters file
  //
  Filename params_file;
  cmdl.addOptionParam(params_file, OPTION_PARAMS_FILE, (Filename)EMPTY);
  
  // add a command line option for the output type
  //
  String output_type;
  cmdl.addOptionParam(output_type, OPTION_OUTPUT_TYPE, OPTION_TYPE_BINARY);

  // add a command line option for the output file
  //
  Filename output_file;
  cmdl.addOptionParam(output_file, OPTION_OUTPUT_FILE, (Filename)EMPTY);

  // add a command line option for the output level
  //
  Long output_level;
  cmdl.addOptionParam(output_level, OPTION_OUTPUT_LEVEL, DEF_OUTPUT_LEVEL);  
  
  // add a command line option for the debug level
  //
  DebugLevel debug_level;
  cmdl.addOptionParam(debug_level, OPTION_DEBUG_LEVEL,
		      DEBUG_LEVEL_DEFAULT);

  // add a command line option for the log verbosity level
  //
  DebugLevel verbosity;
  cmdl.addOptionParam(verbosity, OPTION_VERBOSITY_LEVEL,
		      VERBOSITY_LEVEL_DEFAULT);

  // add a command line option for the log file
  //
  Filename log_file;
  cmdl.addOptionParam(log_file, OPTION_LOG_FILE, (Filename)LOG_FILE_DEFAULT);
  
  // parse the command line
  //
  if (!cmdl.parse(argc, argv)) {
    cmdl.printUsage();
  };

  // open the log file
  //
  if (log_file.ne(L"-")) {
    Console::open(log_file);
    Console::setLineWrap(File::NO_WRAP);
  }

  // print the command line
  //
  if (verbosity >= Integral::BRIEF) {
    cmdl.printCommandLine();
    cmdl.printVersion();
  }

  // check the param file argument
  //
  if(params_file.eq((Filename)EMPTY)) {
    cmdl.printUsage();
  };

  if(output_file.eq((Filename)EMPTY)) {
    cmdl.printUsage();
  };

  if((long)output_level < 0) {
    cmdl.printUsage();
  };    

  // declare local variables
  //
  long pos = 0;
  long num_tokens = 0;
  boolean is_valid = false;

  Sof ofile;
  Sof mfile;
  
  File pfile;  
  Char chr;
  String buf;
  String buffer;
  String output;
  String symbol_name;
  Filename model_file;

  HashTable<String, Filename> model_lookup;
  HashTable<Filename, Long> index_lookup;  
  HashTable<SearchSymbol, Long> symbol_hash;
  Vector<StatisticalModel> stat_models;
  
  // open the parameter file for reading
  //
  if (verbosity >= Integral::BRIEF) {
    Console::increaseIndention();    
    output.assign(L"\nopening parameter file: ");
    output.concat(params_file);
    Console::put(output);
    Console::decreaseIndention();    
  }
  
  if (!pfile.open(params_file, File::READ_ONLY, File::TEXT)) {
    cmdl.printUsage();
  }

  // open the output file for writing
  //
  if (verbosity >= Integral::BRIEF) {
    Console::increaseIndention();    
    output.assign(L"\nopening output file: ");
    output.concat(output_file);
    Console::put(output);
    Console::decreaseIndention();    
  }
  
  if (output_type.eq(OPTION_TYPE_TEXT)) {

    // open the output file in text mode
    //    
    if (!ofile.open(output_file, File::WRITE_ONLY, File::TEXT)) {
      output.assign(L"\ncannot open file: ");
      output.concat(output_file);
      output.concat(L", bailing out...");
      Console::put(output);
      return Integral::exit();
    }
  }

  else {

    // open the output file in binary mode
    //        
    if (!ofile.open(output_file, File::WRITE_ONLY, File::BINARY)) {
      output.assign(L"\ncannot open file: ");
      output.concat(output_file);
      output.concat(L", bailing out...");
      Console::put(output);
      return Integral::exit();
    }
  }
    
  // read the contents of the parameter file line-by-line
  //  
  while (pfile.get(buffer, Sof::BUFFER_SIZE)) {

    // get the first char
    //
    buffer.trim();    
    chr = buffer(0);

    // make sure that the line is valid
    //    
    if (!buffer.eq(EMPTY) && !chr.eq(L'#')) {

      if (debug_level >= Integral::DETAILED) {
	buffer.debug(L"file line");
      }
	  
      // tokenize on the '=' delimiter
      //
      num_tokens = buffer.countTokens(L"=");

      // make sure we have two tokens
      //
      if (num_tokens == 2) {

	pos = 0;
	buffer.tokenize(buf, pos, L"=");
	buf.trim();      

	// set the symbol name
	//
	if (buf.eq(PARAM_SYMBOL)) {

	  buffer.tokenize(buf, pos, L"=");
	  buf.trim();

	  if (debug_level >= Integral::DETAILED) {
	    buf.debug(L"symbol name");
	  }
	  
	  // set the symbol name here
	  //
	  is_valid = true;
	  symbol_name.assign(buf);
	}

	// set the model file name
	//
	if (buf.eq(PARAM_MODEL) && is_valid) {
	  
	  buffer.tokenize(buf, pos, L"=");
	  buf.trim();

	  if (debug_level >= Integral::DETAILED) {
	    buf.debug(L"model file");
	  }
	  
	  // set the symbol name here
	  //
	  is_valid = false;
	  model_file.assign(buf);

	  // add the entry to the lookup table
	  //
	  model_lookup.insert(symbol_name, &model_file);
	}	
      }
    }
  }

  // close the file descriptor
  //
  pfile.close();
  
  // loop over all models and assign them a unique index
  //
  Long index;
  Vector<Filename> values;

  index = 0;
  model_lookup.values(values);

  for (long i=0; i < values.length(); i++) {
    if (!index_lookup.containsKey(values(i))) {

      if (debug_level >= Integral::DETAILED) {
	values(i).debug(L"value");
	index.debug(L"index");
      }
	  
      index_lookup.insert(values(i), &index);
      index++;
    }
  }

  // setup the symbol hash table
  //
  Vector<String> keys;
  model_lookup.keys(keys);

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

    if (debug_level >= Integral::DETAILED) {
      keys(i).debug(L"key");      
      values(i).debug(L"value");
    }
    
    symbol_hash.insert(keys(i), index_lookup.get(values(i)));
  }

  // setup the stat models
  //
  Vector<Long> values1;
  index_lookup.values(values1);

  long num_models = values1.length();
  stat_models.setLength(num_models);

  long model_index = -1;
  long current_file_num = 0;

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

    current_file_num++;
    model_index = (long)(*index_lookup.get(values(i)));

    // open the stat model file
    //
    if (!mfile.open(values(i))) {
      output.assign(L"\ncannot open file: ");
      output.concat(values(i));
      output.concat(L", bailing out...");
      Console::put(output);
      return Integral::exit();
    }

    if (verbosity >= Integral::BRIEF) {
      Console::increaseIndention();
      output.assign(L"\nprocessing file ");
      output.concat(current_file_num);
      output.concat(L" (");
      output.concat(keys(i));
      output.concat(L")");
      output.concat(L": ");
      output.concat(values(i));
      Console::put(output);
      Console::decreaseIndention();	    
    }
    
    // read the stat model file
    //
    if (!stat_models(model_index).read(mfile, (long)0)) {
      output.assign(L"\nerror reading file: ");
      output.concat(values(i));
      output.concat(L", bailing out...");
      Console::put(output);
      return Integral::exit();
    }

    // close the file descriptor
    //    
    mfile.close();
  }

  // write the statistical model pool
  //
  if (!symbol_hash.write(ofile, (long)output_level,
			 SearchLevel::PARAM_STAT_HASH)) {
    output.assign(L"\n error writing to stat hash, bailing out...");
    Console::put(output);    
    return Integral::exit();
  }

  if (!stat_models.write(ofile, (long)output_level,
			 SearchLevel::PARAM_STAT)) {
    output.assign(L"\n error writing to stat models, bailing out...");
    Console::put(output);    
  }  
  
  // close the file descriptor
  //
  ofile.close();

  if (verbosity >= Integral::BRIEF) {    
    output.assign(L"\nprocessed ");
    output.concat(current_file_num);
    output.concat(L" file(s) successfully, attempted ");
    output.concat(current_file_num);
    output.concat(L" file(s)\n");
    Console::put(output);
  }
  
  // close the log file
  //
  if (log_file.ne(L"-")) {
    Console::close();
  }

  // exit gracefully
  //
  return Integral::exit();
}
