// file: $isip/class/pr/LanguageModelXML/lmxml_02.cc
// version: $Id: lmxml_02.cc 10431 2006-02-16 21:32:52Z wholland $

// ISIP include files
//
#include "LanguageModelXML.h"

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
boolean LanguageModelXML::diagnose(Integral::DEBUG level_a) {
  
  //---------------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //---------------------------------------------------------------------------
  
  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(CLASS_NAME);
    output.concat(L": ");
    Console::put(output);
    Console::increaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 1. required public methods
  //
  //--------------------------------------------------------------------------
  
  // set indention
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods...\n");
    Console::increaseIndention();
  }
  
  // test constructor
  //
  LanguageModelXML lmxml;
  
  // test debug methods
  //
  lmxml.setDebug(level_a);
  
  if (level_a > Integral::BRIEF) {
    Console::put(L"testing debug method...");
    lmxml.debug(L"debug");
  }
  
  // set the grammar start tag, so that the clear method has something to
  // clear
  //
  lmxml.grammar_start_tag_d.setType(XMLToken::START_TAG);
  
  // test clear method
  //
  lmxml.clear(Integral::RESET);
  
  if (level_a > Integral::BRIEF) {
    Console::put(L"testing clear method...");
  }
  
  // if the grammar start tag is no longer of type start_tag, then the
  // clear worked.
  //
  if(lmxml.grammar_start_tag_d.getType() != XMLToken::NULL_TAG) {
    return Error::handle(name(), L"clear", Error::TEST, __FILE__, __LINE__);
  }
  
  // test eq method
  //
  LanguageModelXML lmxml2;
  
  if (level_a > Integral::BRIEF) {
    Console::put(L"testing eq method...");
  }
  
  if(!lmxml2.eq(lmxml)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }
  
  // test assign
  //
  lmxml2.grammar_start_tag_d.setType(XMLToken::START_TAG);
  lmxml2.assign(lmxml);
  
  if (level_a > Integral::BRIEF) {
    Console::put(L"testing assign method...");
  }
  
  if(!lmxml2.assign(lmxml)) {
    return Error::handle(name(), L"assign", Error::TEST, __FILE__, __LINE__);
  } 
  
  // reset indention
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     i/o methods. 
  //
  //--------------------------------------------------------------------------
  
#if defined(HAVE_EXPAT)
  
  // set indention
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific protected methods: i/o methods...\n");
    Console::increaseIndention();
  }
  
  // declare variables to store the file names retrieved
  // initialize this with the expanded path and name of the diagnose
  // input list from which all diagnose files will be drawn
  //
  Filename current_file(L"$ISIP_DEVEL/class/pr/LanguageModelXML/diagnose_input_list.sof");
  
  // declare a variable to store the file name with the system
  // variables expanded
  //
  Filename expanded_file;
  
  // expand the file names, if system variables are present
  //
  if(!Integral::expandName(expanded_file, current_file)) {
    Console::put(L"$ISIP_DEVEL not set properly.");
  }
  
  // open the sdb file
  //
  Sof sof_sdb;
  if(!sof_sdb.open(expanded_file, File::READ_ONLY)) {
    return Error::handle(expanded_file, L"open", Error::TEST,
			 __FILE__, __LINE__);
  }  
  
  // read the list of file names
  //
  Sdb sdb;
  sdb.read(sof_sdb, 0);
  
  // we may now close the sdb file, we are finished with it.
  //
  sof_sdb.close();

  long temp_counter=0;
  
  // loop over all files in the sdb list
  //
  for(sdb.gotoFirst();
      sdb.getName(current_file);
      sdb.gotoNext()) {
    
    // expand the file names, if system variables are present
    //
    if(!Integral::expandName(expanded_file, current_file)) {
      String isip_devel_error;
      isip_devel_error.concat(L"$ISIP_DEVEL not set properly.\n");
      isip_devel_error.concat(L"Unable to find diagnose_input_list.sof");
      Console::put(isip_devel_error);
    }
    
    // open the now-absolute file
    //
    Sof sof;
    if(!sof.open(expanded_file, File::READ_ONLY)) {
      return Error::handle(expanded_file, L"open", Error::TEST,
			   __FILE__, __LINE__);
    }
    else {
      if(level_a > Integral::BRIEF) {
	String reading_file;
	reading_file.assign(L"Processing file: ");
	reading_file.concat(expanded_file);
	Console::put(reading_file);
      }
    }
    
    // clear the parser for reuse
    // here it is absolutely necessary to use "FREE" rather
    // than merely "retain", else old context information will
    // not be permanently removed and the second file processed
    // will error.
    //
    lmxml.clear(Integral::FREE);
    
    // reset the debug level
    //
    lmxml.setDebug(level_a);
    
    // set indention for method calls using this file as input
    //
    if (level_a > Integral::NONE) {
      Console::increaseIndention();
    }
    
    lmxml.read(sof, 0);
    
    // close the file
    //
    sof.close();

    // test write function
    //
    String output_filename(L"diagnose_output_");
    output_filename.concat((Long)temp_counter++);
    output_filename.concat(L".sof");    
    sof.open(output_filename, File::WRITE_ONLY);
    lmxml.write(sof, 0);
    sof.close();
    
    
    //--------------------------------------------------------------------------
    //
    // 3. class-specific public methods:
    //     Search related methods.
    //
    //--------------------------------------------------------------------------
    
    //--------------------------------------------------------------------------
    //
    // 4. class-specific protected methods:
    //     Conversion related methods.
    //
    //--------------------------------------------------------------------------    
    
    // execute these methods only if the debug level is at least detailed, and if
    // the grammar being processed has rules on which these methods may act
    //
    if (level_a > Integral::BRIEF &&
	(lmxml.grammar_start_tag_d.getType() != XMLToken::START_AND_END_TAG)) {
      lmxml.displayXMLModel();
    }

    // perform XML->ABNF conversion
    //
    if (level_a > Integral::BRIEF) {
      RuleModel temp_rule_model;
      temp_rule_model=lmxml.getRuleModel();
    }
    
    // reset indention for method calls using this file as input
    //
    if (level_a > Integral::NONE) {
      Console::decreaseIndention();
    }
    
  } // end looping over diagnose input files
  
  // reset indention
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
#endif
  //--------------------------------------------------------------------------
  //
  // 5. Test BNF->XML
  //
  //--------------------------------------------------------------------------    
  // create BNF language model
  //
  RuleModel test_rule_model;
  Vector<ProductionRuleSet> temp_level;
  ProductionRuleSet temp_pr_set;
  ProductionRule temp_pr;

  // construct start rule 1
  //
  temp_pr.setRuleName(L"S");
  temp_pr.setRuleType(ProductionRule::START);
  temp_pr.append(ProductionRuleTokenType::NON_TERMINAL, L"S1", .74);
  temp_pr_set.concat(temp_pr);

  // construct start rule 2
  //
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleName(L"S");
  temp_pr.setRuleType(ProductionRule::START);
  temp_pr.append(ProductionRuleTokenType::NON_TERMINAL, L"S2", .99);
  temp_pr_set.concat(temp_pr);
  
  // construct graph S1
  //
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleType(ProductionRule::NORMAL);
  temp_pr.setRuleName(L"S1");
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"one");
  temp_pr.append(ProductionRuleTokenType::CONCATENATION, L"", .49);
  temp_pr.append(ProductionRuleTokenType::NON_TERMINAL, L"TWO");
  temp_pr_set.concat(temp_pr);

  // construct graph S2
  //
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleName(L"S2");
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"three");
  temp_pr.append(ProductionRuleTokenType::CONCATENATION, L"", .1);
  temp_pr.append(ProductionRuleTokenType::NON_TERMINAL, L"TWO");
  temp_pr_set.concat(temp_pr);

  // construct graph 2
  //
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleName(L"TWO");
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"two");
  temp_pr.append(ProductionRuleTokenType::CONCATENATION, L"", .52);
  temp_pr.append(ProductionRuleTokenType::NON_TERMINAL, L"TWO");
  temp_pr_set.concat(temp_pr);

  // construct graph 3
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleName(L"TWO");
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"two");
  temp_pr.append(ProductionRuleTokenType::CONCATENATION, L"", .52);
  temp_pr.append(ProductionRuleTokenType::OPEN_PAREN, L"", -21.2);
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"a");
  temp_pr.append(ProductionRuleTokenType::ALTERNATION, L"", -14.2);
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"b");
  temp_pr.append(ProductionRuleTokenType::ALTERNATION, L"", .29);
  temp_pr.append(ProductionRuleTokenType::TERMINAL, L"c");
  temp_pr.append(ProductionRuleTokenType::CLOSE_PAREN);
  temp_pr_set.concat(temp_pr);

  // construct graph 4
  //
  temp_pr.clear(Integral::RESET);
  temp_pr.setRuleName(L"TWO");
  temp_pr.append(ProductionRuleTokenType::EPSILON);
  temp_pr_set.concat(temp_pr);

  temp_level.concat(temp_pr_set);
  
  // this rule model now contains the BNF grammar that
  // produces 'one' followed by any number of 'two's
  // S::= one TWO
  // TWO::= two TWO
  // TWO::= epsilon
  //
  test_rule_model.first().concat(temp_level);

  // create a languagemodelxml object and initiate conversion
  //
  LanguageModelXML lmxml_2;
  lmxml_2.setDebug(level_a);
  lmxml_2.setRuleModel(test_rule_model);
  
  //--------------------------------------------------------------------------
  //
  // 6. Clean up.
  //
  //--------------------------------------------------------------------------    
  
  // reset indention
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  if (level_a > Integral::NONE) {
    SysString output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }
  
  // indicate success
  //
  return true;
}
