// file: $isip/class/sp/FtrBuffer/FtrBuffer.h
// version: $Id: FtrBuffer.h 10640 2007-01-27 02:36:04Z tm334 $

//

// this file defines the FtrBuffer class
//

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

// isip include files
//
#ifndef ISIP_HASH_TABLE
#include <HashTable.h>
#endif

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

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

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

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

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

// FtrBuffer: a class used by the AudioFrontEnd class to manage the
// internal buffering of data between Algorithm objects. for example,
// when the Filter algorithm needs to be applied in the signal flow
// graph, the AudioFrontEnd will read the inputs from the FtrBuffer,
// call the compute method of the algorithm, then write the output
// back to the FtrBuffer.
//
class FtrBuffer {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------

  static const String PARAM_HASH;
  static const String PARAM_LENGTH;
  static const String PARAM_FRAME_INDEX;
  static const String PARAM_LAST_INDEX;
  static const String PARAM_COEF_NAME;
  static const String PARAM_LEFTOVER;

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------
  
  // define default value(s) of the class data
  //
  static const float ALL_TIME = -1;

  // ‘FtrBuffer::ALL_TIME’ cannot appear in a constant-expression
  //
  // static const float DEF_END_TIME = ALL_TIME;
  // static const float DEF_CONTEXT_WINDOW = ALL_TIME;
  static const float DEF_END_TIME = -1;
  static const float DEF_CONTEXT_WINDOW = -1;

  static const double DEF_FRAME_DURATION = 0.01;

  // define default argument(s)
  //
  static const long DEF_COEF_BUF_CAPACITY = 512;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 80300;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // define a set of circular buffers to hold the data
  //
  HashTable<String, Vector< CircularBuffer<AlgorithmData> > > hash_d;

  // create space to store how many frames for each data element are needed
  //
  HashTable<String, Long> length_d;

  // maintain an absolute frame reference for the current pointer in
  // each circular buffer. note that the current pointer in the
  // circular buffer will always point to the absolute latest frame
  // that we have any data for, not necessarily the latest frame that
  // we have final data for.
  //
  Long frame_index_d;
  Long last_index_d;
  String coef_name_d;

  // the number of samples in the last frame
  //
  Long leftover_samps_d;

  // the debug level
  //
  static Integral::DEBUG debug_level_d;
  
  // the memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }
  
  static boolean diagnose(Integral::DEBUG level);

  // required methods
  //  the setDebug method for this class is static because the debug_level is
  //  shared across all objects of this class
  //
  boolean debug(const unichar* message) const;

  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG debug_level) {
    debug_level_d = debug_level;
    return true;
  }
  
  // method: destructor
  //
  ~FtrBuffer() {}

  // method: default constructor
  //
  FtrBuffer() {
    hash_d.setAllocationMode(DstrBase::USER);
  }

  // method: copy constructor
  //
  FtrBuffer(const FtrBuffer& arg) {
    assign(arg);
  }

  // assign methods
  //
  boolean assign(const FtrBuffer& arg);

  // i/o methods
  //
  long sofSize() const;
  boolean read(Sof& sof_a, long tag, const String& name = CLASS_NAME);
  boolean write(Sof& sof_a, long tag, const String& name = CLASS_NAME) const;

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

  boolean writeData(Sof& sof_a,
		    const String& param = String::getEmptyString()) const;

  // equality methods
  //
  boolean eq(const FtrBuffer& 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);
  }

  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE);
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  set methods
  //
  //---------------------------------------------------------------------------
  
  // method: setCoefName
  //
  boolean setCoefName(const String& arg) {
    return coef_name_d.assign(arg);
  }

  // method: setFrameIndex
  //
  boolean setFrameIndex(long arg) {
    frame_index_d = arg;
    return true;
  }

  // method: setLastIndex
  //
  boolean setLastIndex(long arg) {
    last_index_d = arg;
    return true;
  }

  // method: setLeftoverSamps
  //
  boolean setLeftoverSamps(long arg) {
    leftover_samps_d = arg;
    return true;
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  get methods
  //
  //---------------------------------------------------------------------------

  // method: getCoefName
  //
  const String& getCoefName() const {
    return coef_name_d;
  }
    
  // method: getFrameIndex
  //
  long getFrameIndex() const {
    return frame_index_d;
  }

  // method: getLastIndex
  //
  long getLastIndex() const {
    return last_index_d;
  }

  // method: getLeftoverSamps
  //
  long getLeftoverSamps() const {
    return leftover_samps_d;
  }
  
  // method: getLength
  //
  long getLength(const String& cname) const {
    return (long)(*length_d.get(cname));
  }

  // method: getLength
  //
  const HashTable<String, Long>& getLength() const {
    return length_d;
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  length access and management methods
  //
  //---------------------------------------------------------------------------

  // method: addOrInitLength
  //
  boolean addOrInitLength(const String& cname, long len = 0) {
    if (length_d.containsKey(cname)) {
      length_d.get(cname)->assign(len);
    }
    else {
      Long l(len);
      length_d.insert(cname, &l);
    }
    return true;
  }

  // method: setLengthToMax
  //
  boolean setLengthToMax(const String& cname, long new_len) {
    if (!length_d.containsKey(cname)) {
      cname.debug(L"cname");
      length_d.debug(L"length_d");
      return Error::handle(name(), L"setLengthToMax",
			   Error::ARG, __FILE__, __LINE__);
    }
    return length_d.get(cname)->max(new_len);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  buffer access and management methods
  //
  //---------------------------------------------------------------------------

  // method: resetBuffer
  //
  boolean resetBuffer();
  
  // method: contains
  //
  boolean contains(const String& arg) const {
    return hash_d.containsKey(arg);
  }

  // method: getBuffer
  //
  Vector< CircularBuffer<AlgorithmData> >& getBuffer() {
    Vector< CircularBuffer<AlgorithmData> >* ptr = hash_d.get(coef_name_d);
    if (ptr == (Vector<CircularBuffer< AlgorithmData> >*)NULL) {
      Error::handle(name(), L"getBuffer",
		    Error::MEM, __FILE__, __LINE__, Error::WARNING);
    }
    return *ptr;
  }
  
  // method: getBuffer
  //
  Vector< CircularBuffer<AlgorithmData> >& getBuffer(const String& cname) {
    Vector< CircularBuffer<AlgorithmData> >* ptr = hash_d.get(cname);
    if (ptr == (Vector< CircularBuffer<AlgorithmData> >*)NULL) {
      cname.debug(L"name is not here");
      Error::handle(name(), L"getBuffer",
		    Error::MEM, __FILE__, __LINE__);
    }
    return *ptr;
  }

  // method: getBuffer
  //
  const Vector< CircularBuffer<AlgorithmData> >& getBuffer() const {
    const Vector< CircularBuffer<AlgorithmData> >* ptr =
      hash_d.get(coef_name_d);
    if (ptr == (Vector<CircularBuffer<AlgorithmData> >*)NULL) {
      Error::handle(name(), L"getBuffer",
		    Error::MEM, __FILE__, __LINE__, Error::WARNING);
    }
    return *ptr;
  }

  // method: getBuffer
  //
  const Vector< CircularBuffer<AlgorithmData> >&
  getBuffer(const String& cname) const {
    const Vector< CircularBuffer<AlgorithmData> >* ptr = hash_d.get(cname);
    if (ptr == (Vector< CircularBuffer<AlgorithmData> >*)NULL) {
      cname.debug(L"name is not here");
      Error::handle(name(), L"getBuffer",
		    Error::MEM, __FILE__, __LINE__);
    }
    return *ptr;
  }

  // method: getData
  //
  AlgorithmData& getData(long ctag, long index, const String& cname) {
    return getBuffer(cname)(ctag)(index);
  }

  // method: getData
  //
  const AlgorithmData& getData(long ctag, long index,
			       const String& cname) const {
    return getBuffer(cname)(ctag)(index);
  }

  // method to ensure a named buffer exists
  //
  boolean addOrInitBuffer(const String& cname, long num_chan,
			  long buf_cap = DEF_COEF_BUF_CAPACITY);

  // method to ensure the given number of channels are initialized
  //
  boolean ensureChannels(const String& cname, long num_chan);
  
  // make sure that the given buffer has the required leading or
  // trailing pad frames.
  //
  boolean ensureBufferTrailingPad(const String& cname, long req_pad);
  boolean ensureBufferLeadingPad(const String& cname, long req_pad);
  
  // advance functions
  //
  boolean advanceRead();
  boolean advanceCurr();

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
};

// end of include file
//
#endif
