// file: $isip/class/dstr/CircularDelayLine/CircularDelayLine.h
// version: $Id: CircularDelayLine.h 7353 2001-09-02 16:57:24Z jelinek $
//

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

// isip include files
//
#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

// CircularDelayLine: a class that implements a tapped delay line using a
// circular buffer. this is a very convenient data structure for many
// functions in digital signal processing. hence, the interface is intended
// to be simple and easy to use for such applications.
//
template<class TObject>
class CircularDelayLine {
  
  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;
   
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  static const String DEF_PARAM;
  static const String PARAM_INDEX;
  static const String PARAM_BUFFER;

  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // define the default value(s) of the class data
  //
  static const long DEF_LENGTH = 1024;
  static const long DEF_INDEX = 0;
  
  // default arguments to methods
  //
  static const long DEF_INCREMENT = 1;
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 41900;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  // allocate internal storage for the buffer
  //
  Vector<TObject> v_d;

  // allocate a pointer for the current location
  //   
  Long index_d;
  
  // define a static debug level for all objects of this class
  //
  static Integral::DEBUG debug_level_d;

  // define the memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // static methods:
  //  diagnose method is moved outside the class header file and
  //  defined in the CircularDelayLineDiagnose.h in order to avoid
  //  issues related to preprocessing of the diagnose code.
  //
  static const String& name();
 
  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG debug_level) {
    debug_level_d = debug_level;
    return true;
  }

  // other debug methods
  //
  boolean debug(const unichar* message) const;
  
  // method: destructor
  //
  ~CircularDelayLine() {}

  // default constructor
  //
  CircularDelayLine(long length = DEF_LENGTH);

  // method: copy constructor
  //
  CircularDelayLine(const CircularDelayLine<TObject>& arg) {
    assign(arg);
  }
  
  // assign methods
  //
  boolean assign(const CircularDelayLine<TObject>& arg);

  // method: operator=
  //
  CircularDelayLine& operator= (const CircularDelayLine& arg) {
    assign(arg);
    return *this;
  }

  // equality methods
  //
  boolean eq(const CircularDelayLine<TObject>& arg) const;

  // method: sofSize
  //
  long sofSize() const {
    long bytes = index_d.sofSize();
    bytes += v_d.sofSize();
    return bytes;
  }

  // method: read
  //
  boolean read(Sof& sof, long tag) {
    return read(sof, tag, name());
  }

  // method: write
  //
  boolean write(Sof& sof, long tag) const {
    return write(sof, tag, name());
  }
  
  // other io methods
  //
  boolean read(Sof& sof, long tag, const String& name);
  boolean write(Sof& sof, long tag, const String& name) const;
  
  boolean readData(Sof& sof, const String& pname = DEF_PARAM,
		   long size = SofParser::FULL_OBJECT,
		   boolean param = true,
		   boolean nested = false);
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) 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);
  }

  // method: clear
  //
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE) {

    // call the vector clear
    //
    v_d.clear(cmode);

    // clear the index in any case
    //
    index_d = DEF_INDEX;
    return true;
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  extensions to required methods
  //
  //---------------------------------------------------------------------------
 
  // method: assign
  //
  boolean assign(const TObject& arg) {
    v_d(index_d) = arg;
    return true;
  }
    
  // method: operator=
  //
  TObject& operator= (const TObject& arg) {
    assign(arg);
    return *this;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  indexing methods
  //
  //---------------------------------------------------------------------------
 
  // method: operator()
  //
  TObject& operator() (long index) {
    long tmp_index = index_d + index;
    tmp_index = wrap(tmp_index);
    return v_d(tmp_index);
  }

  // method: operator()
  //
  const TObject& operator() (long index) const {
    long tmp_index = index_d + index;
    tmp_index = wrap(tmp_index);
    return v_d(tmp_index);
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  size-related methods
  //
  //---------------------------------------------------------------------------

  // method: setLength 
  //
  boolean setLength(long length) {
    return v_d.setLength(length);
  }
  
  // method: length
  //
  long length() const { 
    return v_d.length();
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  core data manipulation methods
  //
  //---------------------------------------------------------------------------
 
  // method: advance
  //
  boolean advance(long increment = DEF_INCREMENT) {
    index_d += increment;
    index_d = wrap(index_d);
    return true;
  }

  // method: advanceAndAssign
  //
  boolean advanceAndAssign(const TObject& input,
			   long increment = DEF_INCREMENT) {
    boolean status = advance(increment);
    v_d(index_d) = input;
    return status;
  }
  
  // method: assignAndAdvance
  //
  boolean assignAndAdvance(const TObject& input,
			   long increment = DEF_INCREMENT) {
    v_d(index_d) = input;
    return advance(increment);
  }

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
    
  // pointer manipulation methods
  //
  long wrap(long value) const;
};

//-----------------------------------------------------------------------------
//
// we define non-integral constants at the end of class definition for
// templates (for non-templates these are defined in the default constructor)
//      
//-----------------------------------------------------------------------------

// constants: required constants such as the class name
//
template <class TObject>
const String CircularDelayLine<TObject>::CLASS_NAME(L"CircularDelayLine");

// constants: define non-integral constants in the default constructor
//
template <class TObject>
const String CircularDelayLine<TObject>::DEF_PARAM(L"");

template <class TObject>
const String CircularDelayLine<TObject>::PARAM_INDEX(L"index");

template <class TObject>
const String CircularDelayLine<TObject>::PARAM_BUFFER(L"buffer");

// static instantiations: debug level
//
template <class TObject>
Integral::DEBUG CircularDelayLine<TObject>::debug_level_d = Integral::NONE;

template <class TObject>
MemoryManager CircularDelayLine<TObject>::mgr_d(sizeof(CircularDelayLine<TObject>),
						CLASS_NAME);

// below are all the methods for the CircularDelayLine template class
//      

//-----------------------------------------------------------------------------
//
// required static methods
//
//-----------------------------------------------------------------------------

// method: name
//
// arguments: none
//
// return: a static String& containing the class name
//
// this method returns the class name
//
template<class TObject>
const String& CircularDelayLine<TObject>::name() {
  
  // create the static name string for this class and return it
  //
  static String cname(CLASS_NAME);
  cname.clear();
  cname.concat(CLASS_NAME);
  cname.concat(L"<");
  cname.concat(TObject::name());
  cname.concat(L">");
  
  // return the name
  //
  return cname;
}

//---------------------------------------------------------------------
//
// required debug methods
//
//----------------------------------------------------------------------

// method: debug
//
// arguments:
//  const unichar* message: (input) information message
//
// return: a boolean value indicating status
//
// this method dumps the contents of an object to the console
// 
template<class TObject>
boolean CircularDelayLine<TObject>::debug(const unichar* message_a) const {
  
  // local variables
  //
  String output;
  String value;
  String param;
  
  // output the current position
  //
  value.assign(index_d);
  output.debugStr(name(), message_a, L"index_d", value);
  Console::put(output);

  // output the debug level
  //
  value.assign(debug_level_d);
  output.debugStr(name(), message_a, L"debug_level_d", value);
  Console::put(output);

  // output a label
  //
  output.assign(L"delay line:");
  Console::put(output);

  // increment the indention level in the console
  //
  Console::increaseIndention();
  
  // output the delay line
  //
  v_d.debug(L"v_d");

  // decrement the indention level in the console
  //
  Console::decreaseIndention();
  
  // exit gracefully
  // 
  return true;
}

//-----------------------------------------------------------------------
//
// default constructor
//
//-----------------------------------------------------------------------

// method: default constructor
//
// arguments:
//  long length: (input) the length of the buffer
//
// return: none
//
// note that the "capacity" and length are the same for this class.
//
template<class TObject>
CircularDelayLine<TObject>::CircularDelayLine(long length_a) {
  
  // initialize the stored item
  //
  v_d.setLength(length_a);
  
  // initialize the position indices
  //
  index_d = DEF_INDEX;
  
  // exit gracefully
  //
}

//-----------------------------------------------------------------------
//
// required assign method(s)
//
//-----------------------------------------------------------------------

// method: assign
//
// arguments:
//  const CircularDelayLine<TObject>& arg: (input) the object to copy
//
// return: a boolean value indicating status
//
template<class TObject>
boolean
CircularDelayLine<TObject>::assign(const CircularDelayLine<TObject>& arg_a) {
  
  // assign the scalar data
  //
  index_d = arg_a.index_d;

  // assign the vector data
  //
  return v_d.assign(arg_a.v_d);
}

//-----------------------------------------------------------------------
//
// required i/o methods
//
//-----------------------------------------------------------------------

// method: read
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
template<class TObject>
boolean CircularDelayLine<TObject>::read(Sof& sof_a, long tag_a,
					 const String& name_a) {
  
  // get the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }

  // exit gracefully
  //
  return true;
}

// method: write
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object write itself to an Sof file
//
template<class TObject>
boolean CircularDelayLine<TObject>::write(Sof& sof_a, long tag_a,
					  const String& name_a) const {
  // declare a temporary size variable
  //
  long obj_size;

  // switch on ascii or binary mode
  //
  if (sof_a.isText()) {
    
    // set the size to be dynamic
    //
    obj_size = Sof::ANY_SIZE;
  }
  else {
    
    // the size of the binary data to write
    //
    obj_size = sofSize();
  }
  
  // put the object into the sof file's index
  //
  if (!sof_a.put(name_a, tag_a, obj_size)) {
    return false;
  }

  // exit gracefully
  //
  return writeData(sof_a);
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size of the object
//  boolean param: (input) is the parameter specified?
//  boolean nested: (input) is this nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
template<class TObject>
boolean CircularDelayLine<TObject>::readData(Sof& sof_a, const String& pname_a,
					     long size_a, boolean param_a,
					     boolean nested_a) {
  
  // declare local variable
  //
  SofParser parser;
  parser.setDebug(debug_level_d);
  
  // ignore implicit parameter setting
  //

  // configure the parser to read a nested object
  //
  if (nested_a) {
    parser.setNest();
  }
  
  // load the parse
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }	    

  // get the index
  //
  if (parser.isPresent(sof_a, PARAM_INDEX)) {
    if (!index_d.readData(sof_a, PARAM_INDEX,
			  parser.getEntry(sof_a, PARAM_INDEX),
			  false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    index_d = DEF_INDEX;
  }
 
  // get the buffer
  //
  if (parser.isPresent(sof_a, PARAM_BUFFER)) {
    if (!v_d.readData(sof_a, PARAM_BUFFER,
		      parser.getEntry(sof_a, PARAM_BUFFER), false, false)) {
      return Error::handle(name(), L"readData", Error::READ,
			   __FILE__, __LINE__, Error::WARNING);
    }
  }
  else {
    v_d.clear();
  }
 
  // exit gracefully
  //
  return true;
}

// method: writeData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//
// return: a boolean value indicating status
//
// this method writes the object to the Sof file. it assumes that the
// Sof file is already positioned correctly.
//
template<class TObject>
boolean CircularDelayLine<TObject>::writeData(Sof& sof_a,
					      const String& pname_a) const {
  
  // write a start string if necessary
  //
  sof_a.writeLabelPrefix(pname_a);

  // write the index
  //
  index_d.writeData(sof_a, PARAM_INDEX);
    
  // write the buffer
  //
  v_d.writeData(sof_a, PARAM_BUFFER);
    
  // put an end string if necessary
  //
  sof_a.writeLabelSuffix(pname_a);

  // exit gracefully
  //
  return true;
}

//------------------------------------------------------------------------
//
// required equality methods
//
//------------------------------------------------------------------------

// method: eq
//
// arguments:
//  const CircularDelayLine<TObject>& arg: (input) the object to compare
//
// return: boolean value indicating status
//
// this method compares two CircularDelayLines for equivalence. two
// CircularDelayLines are equivalent if all corresponding valid items are
// equivalent
//
template<class TObject>
boolean
CircularDelayLine<TObject>::eq(const CircularDelayLine<TObject>& arg_a) const {
  
  // compare positions
  //
  if (index_d != arg_a.index_d) {
    return false;
  }

  // loop over all elements
  //
  long i_end = v_d.length();

  for (long i = 0; i < i_end; i++) {
    if (v_d(i) != arg_a.v_d(i)) {
      return false;
    }
  }

  // equal, exit gracefully
  //
  return true;
}

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

// method: wrap
//
// arguments:
//  long value: (input) unwrapped location
//
// return: a long value containing the wrapped pointer
//
// this method limits a location to the range [0, length]
// 
template<class TObject>
long CircularDelayLine<TObject>::wrap(const long value_a) const {
  
  // fetch the length
  //
  long len = v_d.length();

  // compute the modulus
  //
  long tmp = value_a % len;

  // also wrap negative values
  //
  if (tmp < 0) {
    tmp += len;
  }

  // exit gracefully
  //
  return tmp;
}

// end of include file
//
#endif
