// file: $isip/class/sp/FtrBuffer/ftrb_05.cc
// version: $Id:
//

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

// method: resetBuffer
//
// arguments: none
//
// return: a boolean value indicating status
//
boolean FtrBuffer::resetBuffer() {

  length_d.clear(Integral::RELEASE);
  
  Vector<String> keys;
  hash_d.keys(keys);
  for (long i = keys.length() - 1; i >= 0; i--) {
    Vector< CircularBuffer<AlgorithmData> >* cbuf = hash_d.get(keys(i));

    if (cbuf == (Vector< CircularBuffer<AlgorithmData> >*)NULL) {
      return Error::handle(name(), L"resetBuffer", Error::MEM,
			   __FILE__, __LINE__);
    }

    cbuf->clear(Integral::RELEASE);

    // pull the CircularBuffer out of the HashTable and delete it
    //
    Vector< CircularBuffer<AlgorithmData> >* cbuf0 =
      (Vector< CircularBuffer<AlgorithmData> >*)NULL;
    hash_d.remove(keys(i), cbuf0);
    if (cbuf != cbuf0) {
      return Error::handle(name(), L"resetBuffer", Error::MEM,
			   __FILE__, __LINE__);
    }
    delete cbuf;
  }

  // reset time index
  //
  frame_index_d = -1;
  last_index_d = -1;
  leftover_samps_d = 0;
  
  // exit gracefully
  //
  return true;
}

// method: addOrInitBuffer
//
// arguments:
//  const String& cname: (input) coefficient name
//  long num_chan: (input) number of channels
//  long buf_cap: (input) buffer capacity
//
// return: a boolean value indicating status
//
// ensure that the named buffer exists. if it already exists,
// reinitialize it. if it does not exist, add it.
//
boolean FtrBuffer::addOrInitBuffer(const String& cname_a, long num_chan_a,
				   long buf_cap_a) {

  // check arguments
  //
  if (num_chan_a < 1) {
    return Error::handle(name(), L"addOrInitBuffer", Error::ARG, __FILE__,
			 __LINE__);
  }

  // if the coefficient is currently in the buffer hash, initialize
  // the buffer
  //
  if (hash_d.containsKey(cname_a)) {

    Vector< CircularBuffer<AlgorithmData> >* ptr = &(getBuffer(cname_a));

    // clear out the existing elements
    //
    long nc = ptr->length();
    for (long i = 0; i < nc; i++) {
      (*ptr)(i).clear(Integral::RETAIN);
    }

    ptr->setLength(num_chan_a);

    // clear any new elements
    //
    for (long i = nc; i < num_chan_a; i++) {
      (*ptr)(i).clear(Integral::RETAIN);
    }

    // set the capacity
    //
    for (long i = 0; i < num_chan_a; i++) {
      (*ptr)(i).setCapacity(buf_cap_a);
    }

  }

  // if the coefficient is not currently in the buffer hash, add it
  //
  else {

    Vector< CircularBuffer<AlgorithmData> >* ptr =
      new Vector< CircularBuffer<AlgorithmData> >(num_chan_a);
      
    for (long i = 0; i < num_chan_a; i++) {
      (*ptr)(i).setCapacity(buf_cap_a);
    }
    hash_d.insert(cname_a, ptr);
  }

  // make sure the length hash also contains an entry for this
  // coefficient
  //
  if (!length_d.containsKey(cname_a)) {
    Long obj(-1);
    length_d.insert(cname_a, &obj);
  }
  else {
    length_d.get(cname_a)->assign((long)-1);
  }
    
  // exit gracefully
  //
  return true;
}

// method: ensureChannels
//
// arguments:
//  const String& cname: (input) coefficient name
//  long num_chan: (input) number of channels
//
// return: a boolean value indicating status
//
// ensure that the given buffer has the given channel capacity. if
// there are not enough channels, increase the number of channels
// within the buffer.
//
boolean FtrBuffer::ensureChannels(const String& cname_a, long num_chan_a) {

  Vector< CircularBuffer<AlgorithmData> >* ptr = &(getBuffer(cname_a));

  if (ptr == (Vector< CircularBuffer<AlgorithmData> >*)NULL) {
    return Error::handle(name(), L"ensureChannels", ERR, __FILE__, __LINE__);
  }

  long old_len = ptr->length();

  if (old_len < num_chan_a) {

    // if we are increasing the number of channels, the old length
    // had better be 1 (i.e., not set before).
    //
    if (old_len != 1) {
      return Error::handle(name(), L"ensureChannels", ERR,
			   __FILE__, __LINE__);
    }

    // add the additional channels we need
    //
    ptr->setLength(num_chan_a);

    for (long i = old_len; i < num_chan_a; i++) {
      (*ptr)(i).clear(Integral::RETAIN);
    }

    // set the capacity for each channel to what channel 0 has,
    // since channel 0's capacity was set in the
    // RecipePool::delayRecipe method.
    //
    long buf_cap = (*ptr)(0).getCapacity();
    for (long i = 0; i < num_chan_a; i++) {
      (*ptr)(i).setCapacity(buf_cap);
    }

  }
    
  // exit gracefully
  //
  return true;
}

// method: ensureBufferTrailingPad
//
// arguments:
//  const String& cname: (input) coefficient name
//  long req_pad: (input) required trailing pad (in frames)
//
// return: a boolean value indicating status
//
// make sure that the given buffer has the required number of trailing
// frames. if enough frames do not currently exist, add them.
//
boolean FtrBuffer::ensureBufferTrailingPad(const String& cname_a,
					   long req_pad_a) {

  long nchan = getBuffer(cname_a).length();
  AlgorithmData obj;
  obj.make(getBuffer(cname_a)(0)(0));
  obj.clear(Integral::RETAIN);

  for (long i = 0; i < nchan; i++) {
    while (getBuffer(cname_a)(i).getNumBackward() < req_pad_a) {
      getBuffer(cname_a)(i).prepend(obj);
    }
  }

  // exit gracefully
  //
  return true;
}

// method: ensureBufferLeadingPad
//
// arguments:
//  const String& cname: (input) coefficient name
//  long req_pad: (input) required leading pad (in frames)
//
// return: a boolean value indicating status
//
// make sure that the given buffer has the required number of leading
// frames. if enough frames do not currently exist, add them.
//
boolean FtrBuffer::ensureBufferLeadingPad(const String& cname_a,
					  long req_pad_a) {

  long nchan = getBuffer(cname_a).length();
  AlgorithmData obj;
  obj.make(getBuffer(cname_a)(0)(0));
  obj.clear(Integral::RETAIN);

  for (long i = 0; i < nchan; i++) {
    while (getBuffer(cname_a)(i).getNumForward() < req_pad_a) {
      getBuffer(cname_a)(i).append(obj);
    }
  }

  // exit gracefully
  //
  return true;
}

// method: advanceRead
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method gets the frame duration
//
boolean FtrBuffer::advanceRead() {
  
  Vector<String> ckeys;
  length_d.keys(ckeys);
  for (long i = ckeys.length() - 1; i >= 0; i--) {
    Vector< CircularBuffer<AlgorithmData> >* ptr = hash_d.get(ckeys(i));

    long len = ptr->length();
    long num = (long)(*(length_d.get(ckeys(i))));
    for (long c = 0; c < len; c++) {
      if ((*ptr)(c).getNumElements() > num) {
	(*ptr)(c).setRead(1);
      }
    }
  }
  
  // exit gracefully
  //
  return true;
}

// method: advanceCurr
//
// arguments: none
//
// return: a boolean value indicating status
//
// this method gets the frame duration
//
boolean FtrBuffer::advanceCurr() {
  
  if (getBuffer()(0).getNumElements() > 1) {
    Vector<String> keys;
    hash_d.keys(keys);
    for (long i = keys.length() - 1; i >= 0; i--) {
      if ((*hash_d.get(keys(i)))(0).getNumForward() < 1) {
	keys(i).debug(L"end of coeff buffer");
	printf("this has %ld coeffs\n",
	       (*hash_d.get(keys(i)))(0).getNumElements());
	printf("this needs %ld coeffs\n", (long)(*length_d.get(keys(i))));
	return Error::handle(name(), L"advanceCurr", ERR, __FILE__, __LINE__);
      }
      long nchan = hash_d.get(keys(i))->length();
      for (long c = 0; c < nchan; c++) {
	(*hash_d.get(keys(i)))(c).seekCurr(1);
      }
    }
  }

  // advance the frame index pointer
  //
  frame_index_d += 1;
  
  // exit gracefully
  //
  return true;
}
