// file: $isip/class/mmedia/FeatureFile/ftrf_07.cc
// version: $Id: ftrf_07.cc 10597 2006-08-08 21:28:39Z ss754 $
//

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

/*
// method: getBufferedData
//
// arguments: none
//  Vector<VectorFloat>& data: (output) the features data
//  long start_pos: (input) the start position of the features data
//  
// return: number of data read
//
// this method retrieves data from the circular buffer if it is available,
// or else get thems directly from the feature file.
//
long FeatureFile::getBufferedData(Vector<VectorFloat>& data_a,
				  long start_pos_a) {

  // loop over each channel
  //
  long num_read = 0;
  long num_elem_a = 1;
  
  for (long ctag = 0; ctag < num_channels_d; ctag++) {
    
    // get the data
    //
    long n = getBufferedData(data_a(ctag), ctag, start_pos_a, num_elem_a);

    // error check
    //
    if (n < 0) {
      return -1;
    }
    num_read += n;
  }
  
  // exit gracefully
  //
  return num_read;
}

// method: getBufferedData
//
// arguments:
//  VectorFloat& data: (output) the audio data
//  long ctag: (input) the channel to read
//  long start_pos: (input) the start sample of the audio data
//  long num_elem: (input) the duration of the audio data
//  
// return: number of data read
//
// this method retrieves data from the circular buffer if it is available,
// or else get thems directly from the feature file.
//
long FeatureFile::getBufferedData(VectorFloat& data_a, long ctag_a,
				  long start_pos_a, long num_elem_a) {
  
  // check argument
  //
  if (ctag_a < 0) {
    Error::handle(name(), L"getBufferedData",
		  Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // bad arguments
  //
  if (num_elem_a < 0) {
    Error::handle(name(), L"getBufferedData", Error::ARG, __FILE__, __LINE__);
    return -1;
  }

  // this is a common loop termination condition
  //
  else if (num_elem_a == 0) {
    data_a.clear();
    return -1;
  }
  
  // determine the maximum number of features in the file
  //
  //  long total_nfeatures = getNumFeatures();
  long total_nfeatures = getNumFrames();
  
  // if the required feature index is greater than total features in file,
  // just return 0.
  //
  if (start_pos_a > total_nfeatures - 1) {
    data_a.setLength(0);
    return 0;
  }

  // set the length of vector, it is one
  //
  data_a.setLength(num_elem_a);

  // get the start and end time of the data already in circular buffer
  //
  long buf_duration = buffers_d(ctag_a).getNumElements();
  long buf_end = buf_end_ftr_d(ctag_a);
  long buf_start = getStartFeature(ctag_a);

  long buf_max_sample = buf_end  + (long)buf_size_d - 1;

  // do we need samples that start before the circular buffer starts?
  //
  // or is the start sample much farther in the future, such that the
  // current samples would be pushed out before we get there? 
  //
  // if so we need to reset everything and start over.
  //
  if ((start_pos_a < buf_start) || (start_pos_a > buf_max_sample)) {

    if (debug_level_d >= Integral::BRIEF) {
      String output(L"clearing buffers...");
      Console::put(output);
    }
    
    // clear out buffer
    //
    resetBuffer(ctag_a);
    
    // determine the new starting point that lines up on a block boundary
    //
    buf_start = buf_end_ftr_d(ctag_a) + (long)1;
    buf_duration = 0;

    // set pointers and time indices
    //
    buf_end = buf_start - 1;
    buf_end_ftr_d(ctag_a) = buf_end;
  }

  // do we need to read ?
  //
  while ((!end_of_file_d) &&
	 (start_pos_a > getEndFeature(ctag_a))) {

    // output some debug information
    //
    if (debug_level_d >= Integral::DETAILED) {

      String output;
      String numeric;
      
      output.assign(L"we need to read since ");
      numeric.assign(start_pos_a);
      output.concat(numeric);
      output.concat(L"+");
      numeric.assign(num_elem_a);
      output.concat(numeric);
      output.concat(L"=");
      numeric.assign(start_pos_a + num_elem_a);
      output.concat(numeric);
      output.concat(L" > ");
      numeric.assign(getEndFeature(ctag_a));
      output.concat(numeric);
      Console::put(output);
    }
      
    // pull in the next block of data
    //
    appendData();

    if (debug_level_d >= Integral::ALL) {
      String output;
      output.assign(getEndFeature(ctag_a));
      output.insert(L"now end_samp = ", 0);
      Console::put(output);
    }
    
    // recalculate boundaries
    //
    buf_duration = buffers_d(ctag_a).getNumElements();
    buf_start = getStartFeature(ctag_a);
  }

  // make sure the data is in the buffer
  //
  buf_duration = buffers_d(ctag_a).getNumElements();
  buf_start = getStartFeature(ctag_a);

  // possibly pull data from the circular buffer
  //
  if (start_pos_a < buf_start) {
    Error::handle(name(), L"getBufferedData", ERR, __FILE__, __LINE__);
    return -1;
  }

  if (start_pos_a > buf_end_ftr_d(ctag_a)) {
    Long(start_pos_a).debug(L"start_pos_a");
    buf_end_ftr_d.debug(L"buf_end_samp");
    Error::handle(name(), L"getBufferedData", ERR, __FILE__, __LINE__);
    return -1;
  }
 
  // copy over data from the circular buffer to the output vector
  //
  long buf_index = start_pos_a - buf_end_ftr_d(ctag_a); 
  data_a.assign(buffers_d(ctag_a)(buf_index).getVectorFloat());
    
  // exit gracefully
  //
  return num_elem_a;
}


// method: appendData
//
// arguments: none
//  
// return: a boolean value indicating status
//
// this method reads the next block of data from a feature file and
// places it on the circular buffer.
//
boolean FeatureFile::appendData() {

  // check for end of file
  //
  if (end_of_file_d) {
    return false;
  }
  
  // determine the current location of the buffer
  //
  long first_samp = (long)buf_end_ftr_d(0) + 1;
  long block_samp = block_size_d;
  
  for (long ctag = 0; ctag < num_channels_d; ctag++) {
    
    // determine if we are about to overwrite data
    //
    long num_bytes_left = (buffers_d(ctag).getCapacity() -
			   buffers_d(ctag).getNumElements()) ;

    if (num_bytes_left < block_size_d) {
      
      if (debug_level_d >= Integral::DETAILED) {
	String output(L"Channel ");
	output.concat(ctag);
	output.concat(L": num_bytes_left = ");
	output.concat(num_bytes_left);
	output.concat(L"; clearing out old block");
	Console::put(output);
      }
      
      // clear out the oldest block by advancing the read pointer
      //
      buffers_d(ctag).setRead(block_samp);
    }
  }
  
  // read a block of data
  //
  Vector<AlgorithmData> raw_data;
  
  // read data
  //
  long nsamp = readFeatureData(raw_data, first_samp, block_samp);

  // output debug information
  //
  if (debug_level_d >= Integral::DETAILED) {
    String numeric;
    String output(L"read ");
    numeric.assign(nsamp);
    output.concat(numeric);
    output.concat(L" samples, ending at file position ");
    output.concat(numeric);
    Console::put(output);
  }

  // check for end of file
  //
  if (nsamp != block_samp) {

    // output debug information
    //
    if (debug_level_d >= Integral::DETAILED) {
      String numeric;
      String output(L"end of file at sample ");
      numeric.assign(nsamp + first_samp);
      output.concat(numeric);
      Console::put(output);
    }

    // set the end of file flag
    //
    end_of_file_d = true;
  }

  // transfer data
  //
  for (long i = 0; i < raw_data.length() / num_channels_d ; i++) {
    for (long ctag = 0; ctag < num_channels_d; ctag++) {
      
      // insert the input data into buf
      //
      buffers_d(ctag).append(raw_data(i * num_channels_d + ctag));
      
      // only seek if we can
      //
      if (buffers_d(ctag).getNumElements() > 1) {
	buffers_d(ctag).seekCurr(1);
      }
      
      buf_end_ftr_d(ctag) += 1;
    }
  }
  
  // exit gracefully
  //
  return true;
}
*/

// method: getNumFrames
//
// arguments: none
//
// return: the long value of the number of frames
//
// this method gets the number of frames from a file.
//
long FeatureFile::getNumFrames() {

  long total_nftr = -1; 

  // check if the number of frames was previously cached
  //
  if ((long)num_frames_d != DEF_NUM_FRAMES) {
    return (long)num_frames_d;
  }
  
  // branch on format: RAW
  //
  if (file_format_d == RAW) {

    // RAW FEATURES file BINARY or TEXT
    String output;
    String values;
    long lines = 0;

    // save the current file position and seek end
    //
    long cur_file_pos = raw_features_d.tell();
    
    // branch on type: TEXT
    //
    if (file_type_d == TEXT) {
      raw_features_d.rewind();

      while (!raw_features_d.eof()) {
	lines++;
	values.assign(lines);
	values.concat(L": ");
	raw_features_d.get(output);
	values.concat(output);
      }
      
      raw_features_d.rewind();

      lines--;  // take off one line
      total_nftr = lines / num_channels_d;
      raw_features_d.seek(cur_file_pos, File::POS);
    }

    // type: BINARY
    //
    else if (file_type_d == BINARY) {

      // save the current file position and seek end
      //
      raw_features_d.seek(0, File::POS_PLUS_END);
      long total_len = raw_features_d.tell();
      
      // compute the total number of features
      //
      long nums_per_feature = (num_features_d * num_channels_d);
      total_nftr = total_len / nums_per_feature / sizeof(float64);
      
      // resume the file position
      //
      raw_features_d.seek(cur_file_pos, File::POS);
    }

    // type: unknown
    //
    else {
      return Error::handle(name(), L"getNumFrames", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }
  
  // format: Sof
  //
  else if (file_format_d == SOF) {

    // format: BINARY
    //
    if (in_sof_d.isBinary()) {
      long cur = in_sof_d.tell();
      in_sof_d.seek(sof_length_pos_d, File::POS);
      Long len;
      len.readData(in_sof_d, PARAM_DATA);
      total_nftr = (long)len / (long)num_channels_d;
      in_sof_d.seek(cur, File::POS);
    }

    // format: TEXT
    //
    else if (in_sof_d.isText()) {
      long len =
	in_sof_d.getVecParser().countTokens(SofParser::implicitPname());
      total_nftr = len / (long)num_channels_d;
    }

    // format: unknown
    //
    else {
      return Error::handle(name(), L"getNumFrames", Error::NOT_IMPLEM,
			   __FILE__, __LINE__);
    }
  }

  // format: unknown
  //
  else {
    return Error::handle(name(), L"getNumFrames", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // cache the number of frames
  //
  num_frames_d = total_nftr;
  
  // return the number of frames
  //
  return (long)num_frames_d;
}

/*
// method: readFeatureData
//
// arguments:
//  Vector<VectorFloat>& data: (output) the feature data
//  long ctag: (input) the channel to read
//  long start_pos: (input) the start pos from feature data
//  long num_elem: (input) the num of element read from feature data
//  
// return: number of frames read
//
// this method gets data from an feature file and returns each channel
// as a VectorFloat.
//
long FeatureFile::readFeatureData(Vector<VectorFloat>& data_a,
				  long ctag_a, long start_pos_a,
				  long num_elem_a) {
  
  // make sure the file is open
  //
  if (!isOpen()) {
    return Error::handle(name(), L"", ERR, __FILE__, __LINE__);
  }
  
  // for binary read
  //
  if (file_format_d == RAW) {
    return readRawData(data_a, ctag_a, start_pos_a, num_elem_a);
  }
  
  // sof file
  //
  if (file_format_d == SOF) {
    return readSofData(data_a, ctag_a, start_pos_a, num_elem_a);
  }
  
  // bad type
  //
  Error::handle(name(), L"readFeatureData",
		ERR, __FILE__, __LINE__);
  return -1;
}
*/
