// file: $isip/class/asr/Splitter/splt_05.cc
// version: $Id: splt_05.cc 9965 2005-02-11 21:48:47Z raghavan $
//

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

// method: assign
//
// arguments:
//  const Splitter& arg: (input) splitter object
//
// return: logical error status
//
// this method assigns the input object top the current object
//
boolean Splitter::assign(const Splitter& arg_a) {

  // assign input object to the current object
  //
  mdb_d.assign(arg_a.mdb_d);
  
  // exit gracefully
  //
  return true;
}

// method: clear
//
// arguments:
//  Integral::CMODE cmode: (input) clear mode
//
// return: logical error status
//
// this method assigns the input object top the current object
//
boolean Splitter::clear(Integral::CMODE cmode_a) {

  // clear the member data
  //
  mdb_d.clear(cmode_a);
  
  // exit gracefully
  //
  return true;  
}

// method: eq
//
// arguments:
//  const Splitter& arg: (input) Splitter object
//
// return: logical error status
//
// this method determines if the input object is equal to the current object
//
boolean Splitter::eq(const Splitter& arg_a) const {

  // check if the machine databases arg the same
  //  
  if (!mdb_d.eq(arg_a.mdb_d)) {
    return false;
  }
  
  // exit gracefully
  //
  return true;  
}

// method: createUtterLists
//
// arguments:
//  Filename& identifier_file: (input) identifier file
//  Filename& identifier_list: (input) identifier list name
//  Filename& machine_list: (input) machine list name
//  Filename& accumulator_list: (input) accumulator list name
//  Vector<SingleLinkedList<Filename> >& items: (input) divided utterances
//  Vector<Filename>& identifier_lookup: (output) identifiers
//  Vector<Machine>& machine_lookup: (output) machines
//  Vector<Filename>& accumulator_lookup: (output) accumulators
//
// return: logical error status
//
// this method writes the divided utterance lists to file
//
boolean Splitter::createUtterLists(Filename& identifier_file_a,
				   Filename& identifier_list_a,
				   Filename& machine_list_a,
				   Filename& accumulator_list_a,
				   Vector<SingleLinkedList<Filename> >& items_a,
				   Vector<Filename>& identifier_lookup_a,
				   Vector<Machine>& machine_lookup_a,
				   Vector<Filename>& accumulator_lookup_a) {
  
  // declare local variables
  //
  String file_dir;
  String file_base;
  String tmp_file;
  String file_prefix;
  
  Sdb machines;
  Sdb identifiers;  
  Sdb accumulators;
  
  String output;
  String machine_name;
  
  Filename curr_machine;
  Filename curr_identifier;    
  Filename curr_accumulator;  

  // read in the machine list
  //
  Sof machine_sof;
  if (machine_sof.open(machine_list_a)) {
    if (!machines.read(machine_sof, (long)0)) {
      Error::handle(name(), L"createUtterLists - unable to read machine list", Error::ARG, __FILE__, __LINE__);      
      return false;
    }
    machine_sof.close();
  }
  else {
      Error::handle(name(), L"createUtterLists - unable to open machine list", Error::ARG, __FILE__, __LINE__);          
    return false;
  }

  // read in the identifiers list
  //
  Sof identifier_sof;
  if (identifier_sof.open(identifier_list_a)) {
    if (!identifiers.read(identifier_sof, (long)0)) {
      Error::handle(name(), L"createUtterLists - unable to read machine list", Error::ARG, __FILE__, __LINE__);      
      return false;
    }
    identifier_sof.close();
  }
  else {
      Error::handle(name(), L"createUtterLists - unable to open machine list", Error::ARG, __FILE__, __LINE__);          
    return false;
  }  

  // read in the accumulator list
  //
  Sof accumulator_sof;
  if (accumulator_sof.open(accumulator_list_a)) {
    if (!accumulators.read(accumulator_sof, (long)0)) {
      Error::handle(name(), L"createUtterLists - unable to read accumulator list", Error::ARG, __FILE__, __LINE__);
      return false;
    }
    accumulator_sof.close();
  }
  else {
    Error::handle(name(), L"createUtterLists - unable to open accumulator list", Error::ARG, __FILE__, __LINE__);
    return false;
  }  

  // set the lengths of the lookup lists
  //
  long num_machines = machines.length();

  if (num_machines != accumulators.length()) {
    return Error::handle(name(), L"createUtterLists - mismatch in the number of machines and accumulators", Error::ARG, __FILE__, __LINE__);
  }

  if (num_machines != identifiers.length()) {
    return Error::handle(name(), L"createUtterLists - mismatch in the number of machines and accumulators", Error::ARG, __FILE__, __LINE__);
  }  
  
  identifier_lookup_a.setLength(num_machines);    
  machine_lookup_a.setLength(num_machines);
  accumulator_lookup_a.setLength(num_machines);

  // loop over each accumulator in the accumulator list
  //
  long index = 0;
  for (accumulators.gotoFirst(); accumulators.getName(curr_accumulator);
       accumulators.gotoNext()) {

    // create a fast lookup mechanism for the accumulator names
    //
    accumulator_lookup_a(index++).assign(curr_accumulator);
  }

  // loop over each identifier in the identifier list
  //
  index = 0;
  for (identifiers.gotoFirst(); identifiers.getName(curr_identifier);
       identifiers.gotoNext()) {

    // create a fast lookup mechanism for the identifier names
    //
    identifier_lookup_a(index++).assign(curr_identifier);
  }  
  
  // loop over each machine in the machine list
  //
  index = 0;
  for (machines.gotoFirst(); machines.getName(curr_machine);
       machines.gotoNext()) {

    // create a fast lookup mechanism for the machine names
    //
    machine_name.assign(curr_machine);

    // look up the attributes for the machine in the database
    //
    if (!mdb_d.get(machine_name, machine_lookup_a(index++))) {
      machine_lookup_a(index - 1).setNodeName(machine_name);
      output.assign(L"cannot find attributes for machine: ");
      output.concat(machine_name);
      output.concat(L" using default values");      
      Console::put(output);
    }
  }

  // loop over each of the divided machine lists
  //
  for (long i = 0; i < (long)items_a.length(); i++) {

    // loop over each utterance in the current machine list - append to the sdb
    //
    Sdb output_sdb;
    for (boolean more = items_a(i).gotoFirst(); more;
	 more = items_a(i).gotoNext()) {
      output_sdb.append(*(items_a(i).getCurr()));
    }

    Sof tmp_sof;
    if (tmp_sof.open(identifier_lookup_a(i), File::WRITE_ONLY)) {
      if (!output_sdb.write(tmp_sof, (long)0)) {
	return Error::handle(name(), L"createUtterLists", Error::ARG, __FILE__, __LINE__);
      }
      tmp_sof.close();
    }
    else {
      return Error::handle(name(), L"createUtterLists", Error::ARG, __FILE__, __LINE__);
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: divideAndConquer
//
// arguments:
//  Filename& identifier_file: (input) list of identifiers
//  Filename& machine_list: (input) list of machine names
//  Vector<SingleLinkedList<Filename> >& items: (input) split identifiers
//
// return: logical error status
//
// this method divides the identifier list based on the machine list
//
boolean Splitter::divideAndConquer(Filename& identifier_file_a,
				Filename& machine_list_a,
				Vector<SingleLinkedList<Filename> >& items_a) {

  // declare local variables
  //
  Sdb machines;
  Sdb identifiers;
  Filename input_machine;
  Filename input_identifier;  

  long index = 0;
  double accum = 0.0;
  String machine_name;
  Machine tmp_machine;
  boolean flag = true;
    
  // read in the machine list
  //
  Sof machine_sof;
  if (machine_sof.open(machine_list_a)) {
    if (!machines.read(machine_sof, (long)0)) {
      Error::handle(name(), L"divideAndConquer - unable to open machine list", Error::ARG, __FILE__, __LINE__);
      return false;
    }
    machine_sof.close();
  }
  else {
    Error::handle(name(), L"divideAndConquer - machine list is empty", Error::ARG, __FILE__, __LINE__);
    return false;
  }
  
  // read in the identifier list
  //
  Sof identifier_sof;
  if (identifier_sof.open(identifier_file_a)) {
    if (!identifiers.read(identifier_sof, (long)0)) {
      Error::handle(name(), L"divideAndConquer - unable to open identifier list", Error::ARG, __FILE__, __LINE__);
      return false;
    }
    identifier_sof.close();
  }
  else {
    Error::handle(name(), L"divideAndConquer - identifier list is empty", Error::ARG, __FILE__, __LINE__);
    return false;
  }
       
  // get the number of machines in the list
  //  
  long num_machines = machines.length();

  if (num_machines < 1) {
    Error::handle(name(), L"divideAndConquer - machine list is empty", Error::ARG, __FILE__, __LINE__);
    return false;
  }
  
  // set the length of the input list
  //  
  items_a.setLength(num_machines);

  // split mode: NONE
  //
  if (split_mode_d == NONE) {

    // loop over each file in the identifier list
    //
    index = 0;
    for (identifiers.gotoFirst(); identifiers.getName(input_identifier);
	 identifiers.gotoNext()) {

      // add the identifier to the input list
      //
      if (!items_a(index % num_machines).insertLast(&input_identifier)) {
	return Error::handle(name(), L"divideAndConquer", Error::ARG, __FILE__, __LINE__);
      }
      index++;
    }
  }

  // split mode: SPEED
  //  
  else if (split_mode_d == SPEED) {

    // initialize the random number generator
    //
    srand48((long)27);

    // initialize the vector that represents the machine frequencies
    //
    VectorDouble freq(num_machines);

    // loop over all machines names in the machine list
    //
    for (machines.gotoFirst(); machines.getName(input_machine);
	 machines.gotoNext()) {

      // retrieve the current machine name
      //
      machine_name.assign(input_machine);

      // retrieve the current machine attributes form the database
      //
      if (!mdb_d.get(machine_name, tmp_machine)) {

	// when the machine attributes are not present use defaults
	//
	accum += Machine::DEF_PROCESSOR_SPEED;
	freq(index++) = Machine::DEF_PROCESSOR_SPEED;
      }
      else {

	// retrieve the machine attributes form the database
	//
	VectorDouble psr_speed = tmp_machine.getProcessorSpeed();
	if (psr_speed.length() == 0) {
	  machine_name.debug(L"machine");
	  return Error::handle(name(), L"divideAndConquer - unable to retrieve processor speed form the database", Error::ARG, __FILE__, __LINE__);
	}
	accum += psr_speed(0);
	freq(index++) = psr_speed(0);	
      }
    }

    // compute the frequency
    //
    freq.div(accum);

    // loop over each file in the identifier list
    //
    index = 0;
    for (identifiers.gotoFirst(); identifiers.getName(input_identifier);
	 identifiers.gotoNext()) {

      flag = true;
      
      // add the identifier to the input list
      //
      while (flag) {
	
	double value = drand48();
	if (value < freq(index % num_machines)) {
	  if (!items_a(index % num_machines).insertLast(&input_identifier)) {
	    return Error::handle(name(), L"divideAndConquer", Error::ARG, __FILE__, __LINE__);
	  }
	  flag = false;
	}
	index++;
      }
    }    
  }

  // split mode: MEMORY
  //
  else if (split_mode_d == MEMORY) {
    return Error::handle(name(), L"divideAndConquer - mode not implemented", Error::ARG, __FILE__, __LINE__);
  }

  // split mode: UNKNOWN
  //      
  else {
    return Error::handle(name(), L"divideAndConquer", Error::ARG, __FILE__, __LINE__);
  }
  
  // exit gracefully
  //
  return true;  
}
