// file: $isip/class/stat/SearchLevel/slev_04.cc
// version: $Id: slev_04.cc 10311 2005-12-14 19:29:13Z may $
//

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

// method: sofSize
//
// arguments: none
//
// return: size of object
//
// this method returns the size of the object in the Sof file and is
// used for binary write
//
long SearchLevel::sofSize() const {

  return (level_index_d.sofSize() +
	  level_tag_d.sofSize() +
          nonspeech_boundary_symbol_table_d.sofSize() +
	  nonspeech_internal_symbol_table_d.sofSize() +
	  dummy_symbol_table_d.sofSize() +
	  exclude_symbol_table_d.sofSize() +
	  nsymbol_exclude_symbol_table_d.sofSize() +
	  spenalty_exclude_symbol_table_d.sofSize() +
	  contextless_symbol_table_d.sofSize() +
	  skip_symbol_table_d.sofSize() +
	  non_adapt_symbol_table_d.sofSize() +
	  subGraphSize() +
	  context_map_d.sofSize() +
	  symbol_table_d.sofSize());
}

// 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 SearchLevel::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 SearchLevel::writeData(Sof& sof_a, const String& pname_a) const {
  
  // write a start string if necessary
  //
  sof_a.writeLabelPrefix(pname_a);

  // write the level index
  //
  level_index_d.writeData(sof_a, PARAM_LEVEL_INDEX);

  // write the level tag
  //
  if (!writeLevelTag(sof_a)) {
    return false;
  }
  
  // must write the symbol table first
  //
  if (!writeSymbols(sof_a)) {
    return false;
  }

  // write the non-speech symbols (optional)
  //
  if ((nonspeech_boundary_symbol_table_d.length() > 0) ||
      (nonspeech_internal_symbol_table_d.length() > 0) ||
      sof_a.isBinary()) {
    writeNonSpeechSymbols(sof_a);
  }
  
  // write the dummy symbols (optional)
  //
  if ((dummy_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeDummySymbols(sof_a);
  }
  
  // write the exclude symbols (optional)
  //
  if ((exclude_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeExcludeSymbols(sof_a);
  }
  
  // write the nsymbol exclude symbols (optional)
  //
  if ((nsymbol_exclude_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeNSymbolExcludeSymbols(sof_a);
  }

  // write the spenalty exclude symbols (optional)
  //
  if ((spenalty_exclude_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeSPenaltyExcludeSymbols(sof_a);
  }    
  
  // write the context less symbols (optional)
  //
  if ((contextless_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeContextLessSymbols(sof_a);
  }

  // write the skip symbols (optional)
  //
  if ((skip_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeSkipSymbols(sof_a);
  }    

  // write the non adaptation symbols (optional)
  //
  if ((non_adapt_symbol_table_d.length() > 0) || sof_a.isBinary()) {
    writeNonAdaptSymbols(sof_a);
  }
  
  // write the sub graphs
  //
  if (!writeSubGraphs(sof_a)) {
    return false;
  }

  // write the context mapping table (optional)
  //
  if ((context_map_d.length() > 0) || sof_a.isBinary()) {
    writeContextMapping(sof_a);
  }

  // put an end string if necessary
  //
  sof_a.writeLabelSuffix(pname_a);
  
  // exit gracefully
  //
  return true;
}

// method: writeContextLessSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the ContextLess symbol table from Sof file
//
boolean SearchLevel::writeContextLessSymbols(Sof& sof_a) const {
  
  // write the symbol table and exit
  //
  return contextless_symbol_table_d.writeData(sof_a,
					  PARAM_CONTEXTLESS_SYMBOL);
}


// method: writeSkipSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the Skip symbol table from Sof file
//
boolean SearchLevel::writeSkipSymbols(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return skip_symbol_table_d.writeData(sof_a,
					  PARAM_SKIP_SYMBOL);
}


// method: writeDummySymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the dummy symbol table from Sof file
//
boolean SearchLevel::writeDummySymbols(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return dummy_symbol_table_d.writeData(sof_a, PARAM_DUMMY_SYMBOL);
}


// method: writeNonAdaptSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the dummy symbol table from Sof file
//
boolean SearchLevel::writeNonAdaptSymbols(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return non_adapt_symbol_table_d.writeData(sof_a,
					PARAM_NON_ADAPT_SYMBOL);
}


// method: writeExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the exclude symbol table from Sof file
//
boolean SearchLevel::writeExcludeSymbols(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return exclude_symbol_table_d.writeData(sof_a, PARAM_EXCLUDE_SYMBOL);
}


// method: writeNSymbolExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the nsymbol exclude symbol table from Sof file
//
boolean SearchLevel::writeNSymbolExcludeSymbols(Sof& sof_a) const {
 
  // write the symbol table and exit
  //
  return nsymbol_exclude_symbol_table_d.writeData(sof_a, PARAM_NSYMBOL_EXCLUDE_SYMBOL);
}


// method: writeSPenaltyExcludeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the spenalty exclude symbol table from Sof file
//
boolean SearchLevel::writeSPenaltyExcludeSymbols(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return spenalty_exclude_symbol_table_d.writeData(sof_a, PARAM_SPENALTY_EXCLUDE_SYMBOL);
}


// method: writeLevelTag
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the Skip symbol table from Sof file
//
boolean SearchLevel::writeLevelTag(Sof& sof_a) const {


  
  // write the symbol table and exit
  //
  return level_tag_d.writeData(sof_a, PARAM_LEVEL_TAG);
}


// method: writeSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the symbol table from Sof file
//
boolean SearchLevel::writeSymbols(Sof& sof_a) const {

  // remove the NO_LEFT_CONTEXT and NO_RIGHT_CONTEXT symbols in case
  // these symbols exist in the symbol-table
  //
  long j = 0;
  long len = symbol_table_d.length();

  Vector<SearchSymbol> symbol_table(len);  
  for (long i = 0; i < len; i++) {
    if (!((symbol_table_d(i).eq(SearchSymbol::NO_LEFT_CONTEXT)) ||
	(symbol_table_d(i).eq(SearchSymbol::NO_RIGHT_CONTEXT)))) {
      symbol_table.setLength(j + 1);
      symbol_table(j) = symbol_table_d(i);
      j++;
    }
  }
  
  // write the symbol table and exit
  //
  return symbol_table.writeData(sof_a, PARAM_SYMBOL);
}


// method: writeNonSpeechSymbols
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method writes the non-speech symbol table from Sof file
//
boolean SearchLevel::writeNonSpeechSymbols(Sof& sof_a) const {

  if (nonspeech_boundary_symbol_table_d.length() > 0 || sof_a.isBinary()) {    
    if (!nonspeech_boundary_symbol_table_d.writeData(sof_a, PARAM_NONSPEECH_BOUNDARY_SYMBOL)) {
      return false;
    }
  }
  
  if (nonspeech_internal_symbol_table_d.length() > 0 || sof_a.isBinary()) {    
    if (!nonspeech_internal_symbol_table_d.writeData(sof_a, PARAM_NONSPEECH_INTERNAL_SYMBOL)) {
      return false;
    }
  }
  
  return true;
}

// method: writeContextMapping
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean value indicating status
//
// this method reads context mapping information from an Sof file
//
boolean SearchLevel::writeContextMapping(Sof& sof_a) const {

  // write the context map at this level
  //
  context_map_d.writeData(sof_a, PARAM_CONTEXT_MAPPING);
  
  // gracefully exit
  //
  return true;  
}

// method: subGraphSize
//
// arguments:
//  none
//
// return: an integer indicating size
//
// this finds the size of the subgraphs that will be written to a file.
//
long SearchLevel::subGraphSize() const {

    // make sure the symbol table has been loaded
  //
  long index = 0;
  long dst_index = 0;
  boolean is_eps = false;  
  long num_graphs = sub_graphs_d.length();
  long num_symbols = symbol_table_d.length();
  
  if (num_symbols == 0) {

    // please load symbol table first
    //
    return Error::handle(name(), L"subGraphSize", SearchLevel::ERR,
			 __FILE__, __LINE__);
  }
  
  // declare a Vector of Graph<Ulong> to be written to file
  //
  Ulong tmp_item;
  SearchNode* snode;  
  GraphArc<SearchNode>* dst_arc;
  GraphVertex<SearchNode>* dst_vertex;  
  Vector< DiGraph<Ulong> > tmp_graphs;
  Vector<DiGraph<SearchNode> >sub_graphs;

   // convert each Graph<SearchNode> > to Graph<Ulong>
   //

  sub_graphs.assign(sub_graphs_d);
  
  tmp_graphs.setLength(sub_graphs.length());

  for (long k = 0; k < num_graphs; k++) {

    // set the properties of graphs
    //
    long num_vertices = sub_graphs(k).length() + 2;
    tmp_graphs(k).setWeighted(sub_graphs(k).isWeighted());

    // insert all vertices corresponding the current graph index
    //
    index = 0;
    GraphVertex<SearchNode>* vertices[num_vertices];
    GraphVertex<Ulong>* tmp_vertices[num_vertices];

    // add the start vertices to the arrays
    //
    vertices[index] = sub_graphs(k).getStart();
    tmp_vertices[index++] = tmp_graphs(k).getStart();

    
    
    // add the rest of the vertices to the arrays
    //
    for (boolean more = sub_graphs(k).gotoFirst(); more;
	 more = sub_graphs(k).gotoNext()) {

      // get the current vertex
      //
      vertices[index] = const_cast<GraphVertex<SearchNode>* >(sub_graphs(k).getCurr());
      
      // get the search node corresponding to the vertex
      //
      snode = vertices[index]->getItem();

      // insert a vertex in the Graph<Ulong> for the current vertex
      // using the symbol ID of the vertex as the node item
      //
      tmp_item.assign(snode->getSymbolId());
      tmp_vertices[index++] = tmp_graphs(k).insertVertex(&tmp_item);
    }

    // add the term vertices to the arrays
    //
    vertices[num_vertices - 1] = sub_graphs(k).getTerm();
    tmp_vertices[num_vertices - 1] = tmp_graphs(k).getTerm();
    
    // connect all vertices corresponding to the current graph index
    //
    for (long i = 0; i < num_vertices; i++) {
      boolean arcs_remain = vertices[i]->gotoFirst();
      while (arcs_remain) {

	// get the destination vertex
	//
	dst_arc = vertices[i]->getCurr();
	dst_vertex = dst_arc->getVertex();
	is_eps = dst_arc->getEpsilon();
	
	// find the destination vertex
	//
	dst_index = -1;
	for (long j = 0; j < num_vertices; j++) {
	  if (dst_vertex == vertices[j]) {
	    dst_index = j;
	    break;
	  } 	  
	}

	// error - when we cannot find a destination index
	//
	if (dst_index == -1) {
	  return Error::handle(name(), L"subGraphSize", Error::ARG, __FILE__, __LINE__);
	}

	// connect the vertices in the Graph<Ulong>
	//
	if (!tmp_graphs(k).insertArc(tmp_vertices[i], tmp_vertices[dst_index],
				     is_eps, (double)dst_arc->getWeight())) {
	  return Error::handle(name(), L"subGraphSize", Error::ARG, __FILE__, __LINE__);
	}
	arcs_remain = vertices[i]->gotoNext();
      }
    }
  }

  return tmp_graphs.sofSize();

}


// method: writeSubGraphs
//
// arguments:
//  Sof& sof: (input) sof file object
//
// return: a boolean indicating status
//
// this method has the object write itself from an Sof file
//
boolean SearchLevel::writeSubGraphs(Sof& sof_a) const {

  Vector< DiGraph<Ulong> > tmp_graphs;

  tmp_graphs = convertSubgraphs();

  // write the Vector of Graph<Ulong> to file
  //
  if (!tmp_graphs.writeData(sof_a, PARAM_MODEL)) {
    return false;
  }

  // exit gracefully
  //
  return true;  
}

