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

// make sure definitions are only made once
//
#ifndef ISIP_FEATURE_FILE
#define ISIP_FEATURE_FILE

// isip include files
//
#ifndef ISIP_DOUBLE
#include <Double.h>
#endif

#ifndef ISIP_STRING
#include <String.h>
#endif

#ifndef ISIP_LONG
#include <Long.h>
#endif

#ifndef ISIP_FLOAT
#include <Float.h>
#endif

#ifndef ISIP_FILE
#include <File.h>
#endif

#ifndef ISIP_SOF
#include <Sof.h>
#endif

#ifndef ISIP_ALGORITHM_DATA
#include <AlgorithmData.h>
#endif

#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

#ifndef ISIP_CIRCULAR_BUFFER
#include <CircularBuffer.h>
#endif

#ifndef ISIP_VECTOR_FLOAT
#include <VectorFloat.h>
#endif

#ifndef ISIP_MATRIX_FLOAT
#include <MatrixFloat.h>
#endif

#ifndef ISIP_NAME_MAP
#include <NameMap.h>
#endif

#ifndef ISIP_MEMORY_MANAGER
#include <MemoryManager.h>
#endif

// forward class definitions
//
class Filename;

// FeatureFile: this class is used to manipulate files containing feature
// data. it is used in isip_transform. its function is similar to AudioFile.
//
class FeatureFile {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------
  
  // define the supported file types, file format, data type
  //
  enum FILE_TYPE { TEXT = 0, BINARY, DEF_FILE_TYPE = TEXT };
  enum FILE_FORMAT { SOF = 0, RAW, DEF_FILE_FORMAT = SOF };

  //  compression modes and channel tags
  //
  enum COMP_TYPE { LINEAR = 0, ULAW, ALAW, DEF_COMP_TYPE = LINEAR };

  // enumeration for sample precision
  //
  enum SAMPLE_PRECISION { NONE = 0, USE_SIZE, EIGHT_BITS, TWELVE_BITS,
                          SIXTEEN_BITS, TWENTY_BITS, TWENTY_FOUR_BITS,
                          THIRTY_TWO_BITS, DEF_SAMPLE_PRECISION = NONE };

  // enumeration for the type of data
  //
  enum DATA_TYPE { ALGORITHM_DATA = 0, VECTOR_FLOAT, DEF_DATA_TYPE = ALGORITHM_DATA };

  // define name maps for each of the enumerated values
  //
  static NameMap FILE_TYPE_MAP;
  static NameMap FILE_FORMAT_MAP;
  static NameMap COMP_TYPE_MAP;
  static NameMap SAMPLE_PRECISION_MAP;  
  static NameMap DATA_TYPE_MAP;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------

  static const String PARAM_NAME;
  static const String PARAM_FILE_TYPE;
  static const String PARAM_FILE_FORMAT;
  static const String PARAM_COMP_TYPE;
  
  static const String PARAM_RANGE;
  static const String PARAM_NUM_CHANNELS;
  static const String PARAM_ID;
  static const String PARAM_NUM_FEATURES;
  static const String PARAM_DATA_TYPE;
  static const String PARAM_COEF_TYPE;
  
  static const String PARAM_TAG;
  static const String PARAM_BLOCK_SIZE;
  static const String PARAM_BUF_SIZE;

  static const String PARAM_FRAME_DURATION;
  static const String PARAM_SAMPLE_FREQUENCY;
  static const String PARAM_DATA;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------  

  // define the channel related constants
  //
  static const long CHANNEL_TAG_RIGHT = 1;
  static const long CHANNEL_TAG_LEFT = 0;
  static const long CHANNEL_TAG_ALL = -2;
  static const long DEF_CHANNEL_TAG = CHANNEL_TAG_ALL;

  // define some buffer management constants:
  //  a buffer contains DEF_BUF_SIZE * DEF_BLOCK_SIZE elements
  //
  static const long DEF_BLOCK_SIZE = 8192;
  static const long DEF_BUF_SIZE = 4;         

  static const long DEF_NUM_FEATURES = 1; 
  static const long DEF_START_POS = 0;
  static const long DEF_NUM_ELEM = 4000;
  static const long DEF_TAG = 0;

  // define a default name for feature data
  //
  static const String DEF_FEATURE_NAME;
  
  // define signal related parameters
  //
  static const long DEF_NUM_CHANNELS = 1;
  static const long DEF_NUM_FRAMES = 0;  
  static const float DEF_FRAME_DURATION = 0.01;
  static const float DEF_SAMPLE_FREQ = 8000.0;

  static const float DEF_AMPLITUDE_RANGE = 1.0;
  static const long DEF_SAMPLE_NUM_BYTES = 2;
  
  static const double DEF_START_TIME = 0.0;
  static const double DEF_CENTER_TIME = 0.25;
  static const double DEF_DURATION = 0.5;

  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 50400;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  // define file format-related parameters
  //
  String name_d;                           
  FILE_TYPE file_type_d;
  FILE_FORMAT file_format_d;
  COMP_TYPE compression_type_d;

  // signal-related parameters
  //
  Long num_frames_d;  
  Double amplitude_range_d;
  Long num_channels_d;
  String id_d;
  Long num_features_d;
  DATA_TYPE data_type_d;
  AlgorithmData::COEF_TYPE coef_type_d;

  // Sof file-related parameters
  //
  Long tag_d;
  Sof in_sof_d;

  // buffer-related parameters
  //
  Long block_size_d;
  Long buf_size_d;

  // signal processing-related parameters
  //
  Float frame_duration_d;
  Float sample_frequency_d;

  // define a variable to hold the data
  //
  Vector<AlgorithmData> v_d;
  
  // raw feature file-related parameters
  //
  File raw_features_d;
  //  Vector< CircularBuffer<VectorFloat> > buffers_d;
  Vector< CircularBuffer<AlgorithmData> > buffers_d;  
  Vector<Long> buf_end_ftr_d;
  
  // since the FeatureFile class can either hold configuration
  // information for auxiliary file types (RAW FEATURES) or
  // the actual features data, this flag lets the class keep track of whether
  // data is actually present in the file.
  //
  boolean no_data_d;

  // have we reached the end of file?
  //
  boolean end_of_file_d;

  // if we are writing to a binary Sof file we need to keep track of
  // where we write the FeatureFile object's size and the vector's length
  //
  long sof_length_pos_d;
  
  // declare a static debug level for all class instantiations
  //
  static Integral::DEBUG debug_level_d;
  
  // a static memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:
    
  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }

  // other static methods
  //
  static boolean diagnose(Integral::DEBUG debug_level);
  
  // method: setDebug
  //  the setDebug method for this class is static because the debug_level
  //  is shared across all objects of this class type
  //
  static boolean setDebug(Integral::DEBUG arg) {
    debug_level_d = arg;
    return true;
  }

  // other debug methods
  //
  boolean debug(const unichar* msg) const;

  // method: destructor
  //
  ~FeatureFile() {}

  // method: default constructor
  //
  FeatureFile();

  // method: copy constructor
  //
  FeatureFile(const FeatureFile& arg) {
    num_frames_d = DEF_NUM_FRAMES;    
    num_channels_d = DEF_NUM_CHANNELS;
    data_type_d = DEF_DATA_TYPE;
    coef_type_d = AlgorithmData::DEF_CTYPE;
    assign(arg);
  }

  // assign methods
  //
  boolean assign(const FeatureFile& arg);
  
  // method: operator=
  //
  inline FeatureFile& operator=(const FeatureFile& arg) {
    if (!assign(arg)) {
      Error::handle(name(), L"operator=", Error::ARG,
                    __FILE__, __LINE__);
    }      
    return *this;
  }
  
  // i/o methods
  //
  long sofSize() const;
  
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME);
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME ) const;

  boolean readData(Sof& sof, const String& pname = String::EMPTY,
                   long size = SofParser::FULL_OBJECT,
                   boolean param = true,
                   boolean nested = false);

  boolean writeData(Sof& sof, const String& name = String::EMPTY) const;

  boolean read(long tag, const String& name = CLASS_NAME) {
    return read(in_sof_d, tag, name);
  }
  
  // equality methods:
  //
  boolean eq(const FeatureFile& arg) const;
  
  // method: new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

  // method: new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }

  // method: delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }

  // method: delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }

  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }

  // other memory-management methods
  //
  boolean clear(Integral::CMODE ctype = Integral::DEF_CMODE);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  additional i/o methods
  //
  //---------------------------------------------------------------------------

  // open and close methods:
  //   these also handle clearing the internal data
  //
  boolean open(const Filename& filename, File::MODE mode = File::READ_ONLY);
  boolean open(const unichar* filename, File::MODE mode = File::READ_ONLY);
  boolean close();
  boolean isOpen() const;

  // methods that get features from buffers or files
  //
  long getBufferedData(Vector<AlgorithmData>& data,
		       long start_pos = DEF_START_POS,
		       long num_elem = DEF_NUM_ELEM);

  long getBufferedData(Vector<VectorFloat>& data,
		       long start_pos = DEF_START_POS);

  long getBufferedData(VectorFloat& data,
		       long channel_tag,
		       long start_pos = DEF_START_POS,
		       long num_elem = DEF_NUM_ELEM);
  
  long readFeatureData(Vector<AlgorithmData>& data,
		       long start_pos = DEF_START_POS,
		       long num_elem = DEF_NUM_ELEM);

  long readFeatureData(Vector<VectorFloat>& data,
  		       long channel_tag = DEF_CHANNEL_TAG,
  		       long start_pos = DEF_START_POS,
		       long num_elem = DEF_NUM_ELEM);

  // methods that write features to a file
  //
  long writeFeatureData(Vector<AlgorithmData>& data,
			long channel_tag = DEF_CHANNEL_TAG);
  
  long writeFeatureData(Vector<VectorFloat>& data,
  			long channel_tag = DEF_CHANNEL_TAG);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  indexing methods
  //
  //---------------------------------------------------------------------------

  // method: operator()
  //
  AlgorithmData& operator()(long index) {
    return v_d(index);
  }

  // method: operator()
  //
  const AlgorithmData& operator()(long index) const {
    return v_d(index);
  }

  // method: operator()
  //
  AlgorithmData& operator()(long ctag, long frame) {
    return v_d(frame * num_channels_d + ctag);
  }

  // method: operator()
  //
  const AlgorithmData& operator()(long ctag, long frame) const {
    return v_d(frame * num_channels_d + ctag);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  set methods
  //
  //---------------------------------------------------------------------------  
  // method: setName
  //
  boolean setName(const String& arg) {
    return name_d.assign(arg);
  }

  // method: setFileType
  //
  boolean setFileType(FILE_TYPE file_type) {
    file_type_d = file_type;
    return true;
  }
  
  // method: setFileFormat
  //
  boolean setFileFormat(FILE_FORMAT file_format) {
    file_format_d = file_format;
    return true;
  }

  // method: setCompType
  //
  boolean setCompType(COMP_TYPE comp_type) {
    if (comp_type != LINEAR) {
      return Error::handle(name(), L"setCompType", Error::NOT_IMPLEM,
                           __FILE__, __LINE__);
    }
    compression_type_d = comp_type;
    return true;
  }

  // method: setAmplitudeRange
  //
  boolean setAmplitudeRange(double amplitude_range) {
    amplitude_range_d = amplitude_range;
    return true;
  }

  // method: setNumChannels
  //
  boolean setNumChannels(long arg) {
    return num_channels_d.assign(arg);
  }

  // method: setID
  //
  boolean setID(const String& arg) {
    return id_d.assign(arg);
  }

  // method: setNumFeatures
  //
  boolean setNumFeatures(long arg) {
    num_features_d = arg;
    return true;
  }   

  // method: setDataType
  //
  boolean setDataType(DATA_TYPE arg) {
    data_type_d = arg;
    return true;
  }
  
  // method: setCoefType
  //
  boolean setCoefType(AlgorithmData::COEF_TYPE arg) {
    coef_type_d = arg;
    return true;
  }
  
  // method: setFrameDuration
  //
  boolean setFrameDuration(float arg) {
    frame_duration_d = arg;
    return true;
  }

  // method: setSampleFrequency
  //
  boolean setSampleFrequency(float arg) {
    sample_frequency_d = arg;
    return true;
  }

  // method: setLength
  //
  boolean setLength(long arg) {
    v_d.setLength(arg);
    return true;
  }
  
  // other set methods
  //
  boolean setBufferSize(long nblocks);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  get methods
  //
  //---------------------------------------------------------------------------

  // method: getName
  //
  const String& getName() const {
    return name_d;
  }

  // method: getFileType
  //
  FILE_TYPE getFileType() const {
    return file_type_d;
  }

  // method: getFileFormat
  //
  FILE_FORMAT getFileFormat() const {
    return file_format_d;
  }

  // method: getCompType
  //
  COMP_TYPE getCompType() const {
    return compression_type_d;
  }

  // method: getAmplitudeRange
  //
  double getAmplitudeRange() const {
    return amplitude_range_d;
  }

  // method: getNumChannels
  //
  long getNumChannels() const {
    return num_channels_d;
  }

  // method: getID
  //
  const String& getID() const {
    return id_d;
  }

  // method: getNumFeatures
  //
  long getNumFeatures() const {
    return num_features_d;
  }
  
  // method: getDataType
  //
  DATA_TYPE getDataType() const {
    return data_type_d;
  }

  // method: getCoefType
  //
  AlgorithmData::COEF_TYPE getCoefType() const {
    return coef_type_d;
  }
  
  // method: getFrameDuration
  //
  float getFrameDuration() const {
    return frame_duration_d;
  }

  // method: getSampleFrequency
  //
  float getSampleFrequency() const {
    return sample_frequency_d;
  }

  // method: getNumFrames
  //
  long getNumFrames(const Sof& sof) const {
    return (sof.getVecSize() / num_channels_d);
  }

  // other get methods
  //
  long getNumFrames();

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  partial i/o methods
  //
  //---------------------------------------------------------------------------

  // method: readStart
  //
  boolean readStart(const String& pname = String::EMPTY,
		    long size = SofParser::FULL_OBJECT,
		    boolean param = true,  boolean nested = true) {
    in_sof_d.find(name(), (long) tag_d);
    return readStart(in_sof_d, pname, size, param, nested);
  }

  // method: readTerminate
  //
  boolean readTerminate() {
    return in_sof_d.stopPartialRead();
  }
  
  // other partial read methods
  //
  boolean readPartialData(long start_pos, long num_elem);
  
  // method: writeStart
  //
  boolean writeStart(const String& pname = String::EMPTY);
  
  // method: writeTerminate
  //
  boolean writeTerminate(const String& pname = String::EMPTY) {
    writeTerminate(in_sof_d, pname);
    return true;
  }
  
  // other partial write methods
  //
  boolean writePartialData(long start_pos, long num_elem);
   
  // almostEqual function
  //
  boolean almostEqual(const FeatureFile& arg,
		      double percent = Integral::DEF_PERCENTAGE,
		      double bound = Integral::DEF_BOUND) const;

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

  // method: getStartFeature
  //
  long getStartFeature(long ctag = DEF_CHANNEL_TAG) const {

    long buf_duration = buffers_d(ctag).getNumElements();
    long buf_end = buf_end_ftr_d(ctag);
    
    // calculate and return the start time
    //
    return (buf_end - buf_duration);
  }

  // method: getEndFeature
  //
  long getEndFeature(long ctag = DEF_CHANNEL_TAG) const {
    return buf_end_ftr_d(ctag);
  }
  
  // append methods
  //
  boolean appendData();
  
  // configuration i/o methods:
  //  this method reads and writes only the configuration parameters. it is
  //  called by both readData and readStart
  //
  boolean readConfig(Sof& sof, SofParser& parser);
  boolean writeConfig(Sof& sof) const;

  // feature file format i/o methods:
  // read data of a certain number from the feature file, in the
  // default case, read the whole file
  //
  long readRawData(Vector<AlgorithmData>& data,
		   long start_pos = DEF_START_POS,
		   long num_samp = DEF_NUM_ELEM);

  //  long readRawData(Vector<VectorFloat>& data_a, long ctag_a,
  //	   long start_samp_a, long num_samp_a);

  long writeRawData(Vector<AlgorithmData>& data_a,
		    long ctag_a);

  //  long writeRawData(Vector<VectorFloat>& data_a,
  //		    long ctag_a);


  long readSofData(Vector<AlgorithmData>& data,
                   long start_pos = DEF_START_POS,
                   long num_elem = DEF_NUM_ELEM);

  //  long readSofData(Vector<VectorFloat>& data_a, long ctag_a,
  //		   long start_samp_a, long num_samp_a);
  
  long writeSofData(Vector<AlgorithmData>& data,
                    long channel_tag = DEF_CHANNEL_TAG);

  //  long writeSofData(Vector<VectorFloat>& data,
  //                    long channel_tag = DEF_CHANNEL_TAG);  

  boolean readSofStart();
  boolean writeSofStart();

  // partial i/o methods:
  //
  boolean readStart(Sof& sof, const String& pname = String::EMPTY,
		    long size = SofParser::FULL_OBJECT,
		    boolean param = true,  boolean nested = true);

  boolean writeStart(Sof& sof, const String& pname = String::EMPTY) const;
  boolean writeTerminate(Sof& sof, const String& pname = String::EMPTY) const;

  // reset methods: clear out buffer and pointers
  //
  boolean resetBuffer(long channel_tag = CHANNEL_TAG_ALL);

};

// end of include file
// 
#endif

