// file: $isip/class/mmedia/FeatureSelect/feats_06.cc
// version: $Id: feats_06.cc 10599 2006-08-08 22:03:18Z ss754 $
//

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

// method: getAllFeatures
//
// arguments:
//  Sdb& symbols: (input) list of symbols
//  long num_feat_vectors: (input) number of feature vectors
//                               corresponding to each symbol
//  Vector<VectorFloat>& features: (output) segmental features
//
// return: a boolean value indicating status
//
// this method gets the feature vectors corresponding to the input
// symbols
//
boolean FeatureSelect::getAllFeatures(Sdb& symbols_a, long num_feat_vectors_a,
				      Vector<VectorFloat>& features_a) {

  // error checking
  //
  if (symbols_a.length() <= (long)0) {
    String msg(L"Error: no symbol specified ");
    Console::put(msg);
    Error::handle(name(), L"getAllFeatures", ERR, __FILE__, __LINE__);
  }  
  
  // increase the identation
  //
  if (verbosity_d >= Integral::BRIEF) {
    Console::increaseIndention();
  }

  // local variables
  //
  Filename filename;
  String symbol;

  // loop over all the symbols and get the feature vector for each
  //
  for (boolean more = symbols_a.gotoFirst(); more;
       more = symbols_a.gotoNext()) {

    Vector<VectorFloat> temp_features;
    symbols_a.getName(filename);
    symbol.assign(filename);
    
    // display a status message
    //
    if (verbosity_d >= Integral::BRIEF) {
      String output(L"selecting ");
      output.concat(num_feat_vectors_a);
      output.concat(L" feature vectors for symbol ");
      output.concat(symbol);
      Console::putNoWrap(output);
    }  

    getFeatures(symbol, num_feat_vectors_a, temp_features);
    features_a.concat(temp_features);
  }
  
  // decrease the identation
  //
  if (verbosity_d >= Integral::BRIEF) {
    Console::decreaseIndention();
  }  

  // exit gracefully
  //
  return true;
}

// method: getFeatures
//
// arguments:
//  String& symbol: (input) symbol
//  long num_feat_vectors: (input) number of feature vectors
//                         corresponding to the input symbol
//  Vector<VectorFloat>& features: (output) segmental features cooresponding
//                                 to the input symbol
//
// return: a boolean value indicating status
//
// this method gets the feature vectors corresponding to the input
// symbol
//
// Note: this function assumes that the database already opened before
// this function is called
//
boolean FeatureSelect::getFeatures(String& symbol_a, long num_feat_vectors_a,
				   Vector<VectorFloat>& features_a) {

  // error checking
  //
  if (symbol_a.length() <= (long)0) {
    String msg(L"Error: no symbol specified ");
    Console::put(msg);
    Error::handle(name(), L"getFeatures", ERR, __FILE__, __LINE__);
  }  

  // local variables
  //
  Vector<VectorFloat> temp_features;  
  Filename ftr_file;
  
  // retrieve the features for the current identifier
  //  
  if (!retrieveFtrFile(symbol_a, ftr_file)) {
    String msg(L"Error: cannot retrieve feature file for identifier: ");
    msg.concat(symbol_a);
    msg.concat(L" from the audio database: ");
    msg.concat(audiodb_file_d);
    Console::put(msg);
    Error::handle(name(), L"getFeatures", ERR, __FILE__, __LINE__);
  }      
  if (!readFeatures(ftr_file, features_a)) {
    String msg(L"Error: cannot read features from feature file: ");
    msg.concat(ftr_file);
    Console::put(msg);
    Error::handle(name(), L"getFeatures", ERR, __FILE__, __LINE__);
  }

  // if the number of features in the file are greater than the
  // required, randomly select the number of features that are
  // required
  //
  if (features_a.length() > num_feat_vectors_a) {

    // local variables
    //
    long num_items = (long)0;
    //    Random random(Random::UNIFORM, Random::SUBTRACTIVE);
    Vector<Long> keys;
    HashTable<Long, Long> hashtable;
    Long one = (Long)1;
    /*    
    // compute the random seed from the time
    //
    Long rand_seed = (Long)Integral::time();
    random.seed((long)rand_seed);    
    
    // seed the random variable
    //
    random.seed((long)rand_seed);
    */
    while (num_items < num_feat_vectors_a) {
      
      // get the random number and scale it
      //
      //      long index = (long)floor(random.get() * features_a.length());
      long index = (long)floor(Random::GLOBAL_UNIFORM.get()
			       * features_a.length());
      
      // add index as a key to the hashtable is it does not exists
      //
      if (!hashtable.containsKey(index)) {
	hashtable.insert(index, &one);
	num_items++;
      }
    }
    
    // get all the indices (keys) and sort them
    //
    hashtable.keys(keys);
    keys.sort();
    Long len = (Long)(features_a.length());
    
    // accumulate all the features corresponding to the indices
    //
    for (long i = 0; i < num_feat_vectors_a; i++) {
      temp_features.concat(features_a(keys(i)));      
    }

    // assign the required features
    //
    features_a.clear();
    features_a.setCapacity(temp_features.length());
    features_a.assign(temp_features);  
  }
  
  // exit gracefully
  //
  return true;
}

// method: openAudioDatabase
//
// arguments:
//  none
//
// return: a boolean value indicating status
//
// this method opens the audio database
//
boolean FeatureSelect::openAudioDatabase() {
  
  // open the audio database
  //
  audiodb_d.setDebug(debug_level_d);
  
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"opening audio database: ");
    output.concat(audiodb_file_d);
    Console::putNoWrap(output);
  }  
  if (!audiodb_d.open(audiodb_file_d)) {
    String msg(L"unable to open audio database (\n");
    msg.concat(audiodb_file_d);
    msg.concat(L")");
    Console::put(msg);
    Error::handle(name(), L"openAudioDatabase",
		  Error::ARG, __FILE__, __LINE__);
  }

  // exit gracefully
  //
  return true;
}

// method: closeAudioDatabase
//
// arguments:
//  none
//
// return: a boolean value indicating status
//
// this method closes the audio database
//
boolean FeatureSelect::closeAudioDatabase() {
  
  // close the audio database
  //
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"closing audio database: ");
    output.concat(audiodb_file_d);
    Console::putNoWrap(output);
  }
  
  if (!audiodb_d.close()) {
    String msg(L"unable to close audio database (\n");
    msg.concat(audiodb_file_d);
    msg.concat(L")");
    Console::put(msg);
    Error::handle(name(), L"closeAudioDatabase",
		  Error::ARG, __FILE__, __LINE__);
  }
    
  // exit gracefully
  //
  return true;
}

// method: retrieveFtrFile
//
// arguments:
//  const String& identifier: (input) identifier
//  Filename& ftr_file: (output) feature file corresponding to the
//                               identifier
// return: a boolean value indicating status
//
// this method retrieves the feature file from audio database
//
boolean FeatureSelect::retrieveFtrFile(const String& identifier_a,
				       Filename& ftr_file_a) {
  
  // error checking
  //
  if (identifier_a.length() <= (long)0) {
    String msg(L"no input identifier");
    Console::putNoWrap(msg);
    Error::handle(name(), L"retrieveFtrFile", ERR, __FILE__, __LINE__);
  }
  
  // display a status message
  //
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"retrieving feature file for ");
    output.concat(L"identifier ");
    output.concat(identifier_a);
    Console::putNoWrap(output);
  }
  
  // retrieve the feature-file corresponding to the identifier
  //
  String ident(identifier_a);
  if (!audiodb_d.getRecord(ident, ftr_file_a)) {
    String msg(L"no file for identifier ");
    msg.concat(identifier_a);
    msg.concat(L" in the audio database");
    Console::putNoWrap(msg);
    Error::handle(name(), L"retrieveFtrFile",
		  Error::ARG, __FILE__, __LINE__);
  }
  
  // display a status message
  //
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"retrieved feature file for identifier ");
    output.concat(identifier_a);
    output.concat(L" is ");
    output.concat(ftr_file_a);
    Console::putNoWrap(output);
  }
  
  // exit gracefully
  //
  return true;
}

// method: readFeatures
//
// arguments:
//  const Filename& ftr_file: (input) feature file
//  Vector<VectorFloat>& features: (output)features corresponding to the
//                                  identifier
// return: a boolean value indicating status
//
// this method reads the features from the feature file
//
boolean FeatureSelect::readFeatures(const Filename& ftr_file_a,
				    Vector<VectorFloat>& features_a) {
  
  // error checking
  //
  if (ftr_file_a.length() <= (long)0) {
    String msg(L"no input feature file");
    Console::putNoWrap(msg);
    Error::handle(name(), L"readFeatures", ERR, __FILE__, __LINE__);
  }
  
  // local variables
  //
  FeatureFile ftr_file_in;
  Filename input_file;

  // set the feature file type and format
  //
  ftr_file_in.setFileType(input_type_d);
  
  if (input_format_d == FeatureFile::RAW) {
    ftr_file_in.setNumFeatures(dim_d);
    ftr_file_in.setFileFormat(input_format_d);
    // ftr_file.setNumChannels(channel_a);
  }    
  
  // display a status message
  //
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"reading feature file: ");
    output.concat(ftr_file_a);
    Console::putNoWrap(output);
  }
  
  // open the feature file, and read the header if sof
  //
  ftr_file_in.open(ftr_file_a);
  
  // get the dimensions of sof file
  //
  if (output_format_d == FeatureFile::SOF) {
    
    // set the file format
    //
    dim_d = ftr_file_in.getNumFeatures();
  }
  
  // get the feature-vectors from all the channels
  //
  long num = ftr_file_in.getNumFrames();

  // set FeatureFile object's data type as VectorFloat
  //
  ftr_file_in.setDataType(FeatureFile::VECTOR_FLOAT);
  
  if (ftr_file_in.readFeatureData(features_a, (long)0,
				  FeatureFile::DEF_START_POS,
				  num) != num) {
    String msg(L"Error: can't read features from feature file: ");
    msg.concat(ftr_file_a);
    Console::put(msg);
    Error::handle(name(), L"readFeatures", ERR, __FILE__, __LINE__);
  }
  
  // close the feature file
  //
  ftr_file_in.close();
  
  // exit gracefully
  //
  return true;
}

// method: writeFeatures
//
// arguments:
//  const Filename& ftrfile_name: (input) name of the output feature file
//  const Vector<VectorFloat>& features: (input) features
//
// return: a boolean value indicating status
//
// this method write features to an output feature file
//
boolean FeatureSelect::writeFeatures(const Filename& ftrfile_name_a,
				     const Vector<VectorFloat>& features_a) {
  
  // error checking
  //
  if (ftrfile_name_a.length() <= (long)0) {
    String msg(L"no input feature file name");
    Console::putNoWrap(msg);
    Error::handle(name(), L"writeFeatures", ERR, __FILE__, __LINE__);
  }  
  if (features_a.length() <= (long)0) {
    String msg(L"no input features");
    Console::putNoWrap(msg);
    Error::handle(name(), L"writeFeatures", ERR, __FILE__, __LINE__);
  }
  
  // local variables
  //
  FeatureFile ftrfile;
  
  // set the feature file type and format
  //
  ftrfile.setFileType(output_type_d);
  
  ftrfile.setNumFeatures(dim_d);
  ftrfile.setFileFormat(output_format_d);
  
  // display a status message
  //
  if (verbosity_d >= Integral::BRIEF) {
    String output(L"writing features to the feature file: ");
    output.concat(ftrfile_name_a);
    Console::putNoWrap(output);
  }
  
  // open the feature file
  //
  ftrfile.open(ftrfile_name_a, File::WRITE_ONLY);
  
  // get the feature-vectors to the feature file
  //
  Vector<VectorFloat> temp_features(features_a);

  // set FeatureFile object's data type as VectorFloat
  //
  ftrfile.setDataType(FeatureFile::VECTOR_FLOAT);
  
  if (!ftrfile.writeFeatureData(temp_features)) {
    String msg(L"Error: cannot write features to feature file");
    Console::put(msg);
    Error::handle(name(), L"writeFeatures", ERR, __FILE__, __LINE__);
  }
  
  // close the feature file
  //
  ftrfile.close();
  
  // exit gracefully
  //
  return true;
}
