// file: $isip/class/dstr/CircularDelayLine/CircularDelayLineDiagnose.h
// version: $Id: CircularDelayLineDiagnose.h 10636 2007-01-26 22:18:09Z tm334 $
//

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

// isip include files
//
#ifndef ISIP_CIRCULAR_DELAY_LINE
#include <CircularDelayLine.h>
#endif

// CircularDelayLineDiagnose: a class that contains the diagnose
// method of CircularDelayLine class.
//
template<class TObject>
class CircularDelayLineDiagnose : public CircularDelayLine<TObject> {
  
  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
   
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // define the default value(s) of the class data
  //

  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // method: name
  //
  static const String& name() {
    return CircularDelayLine<TObject>::name();
  }

  // other static methods
  //  
  static boolean diagnose(Integral::DEBUG debug_level);

  // debug methods
  //  these methods are omitted since this class does not have data
  //  members and operations  
  //
  
  // destructor/constructor(s):
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // assign methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // operator= methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //  

  // i/o methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // equality methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

  // memory-management methods:
  //  these methods are omitted since this class does not have data
  //  members and operations
  //

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

  //  these methods are omitted since this class does not have data
  //  members and operations
  //
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

};

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

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

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
template<class TObject>
boolean CircularDelayLineDiagnose<TObject>::diagnose(Integral::DEBUG level_a) {
  
  //---------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //---------------------------------------------------------------------
  
  // output the class name
  //
  if (level_a > Integral::NONE) {
    String output(L"diagnosing class ");
    output.concat(CircularDelayLine<TObject>::CLASS_NAME);
    output.concat(L": ");
    Console::put(output);
    Console::increaseIndention();
  }

  //--------------------------------------------------------------------
  //
  // 1. required public methods
  //
  //--------------------------------------------------------------------
  
  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods...\n");
    Console::increaseIndention();
  }

  // constructors
  //
  CircularDelayLine<Float> cdl_1(2);
  CircularDelayLine<Float> cdl_2(cdl_1);
  
  if (!cdl_2.eq(cdl_1)) {
    return Error::handle(name(), L"copy constructor/eq", Error::TEST,
			 __FILE__, __LINE__);
  }
   
  // set the debug level to itself
  //
  CircularDelayLine<TObject>::setDebug(CircularDelayLine<TObject>::debug_level_d);
  
  // testing memory allocation
  //
  CircularDelayLine<TObject>::setGrowSize((long)500);

  for (long j = 1; j <= 10; j++) {
    CircularDelayLine<TObject>** dyn_auto = new CircularDelayLine<TObject>*[j * 100];
    
    // create the objects
    //
    for (long i = 0; i < j * 100; i++) {
      dyn_auto[i] = new CircularDelayLine<TObject>();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete dyn_auto[i];
    }
      
    delete [] dyn_auto;
  }
 
  // test the i/o methods 
  //
  CircularDelayLine<Float> cdl_3(3);
  CircularDelayLine<Float> cdl_4;
  CircularDelayLine<Float> cdl_5;

  // we need binary and text sof files
  //
  String tmp_filename0;
  Integral::makeTemp(tmp_filename0);
  String tmp_filename1;
  Integral::makeTemp(tmp_filename1);

  // open files in write mode
  //
  Sof tmp_file0;
  tmp_file0.open(tmp_filename0, File::WRITE_ONLY, File::TEXT);
  Sof tmp_file1;
  tmp_file1.open(tmp_filename1, File::WRITE_ONLY, File::BINARY);

  cdl_3.write(tmp_file0, (long)0);
  cdl_3.write(tmp_file1, (long)0);
  
  // close the files
  //
  tmp_file0.close();
  tmp_file1.close();

  // open the files in read mode
  //
  tmp_file0.open(tmp_filename0);
  tmp_file1.open(tmp_filename1);

  // read the value back
  //
  if (!cdl_4.read(tmp_file0, (long)0) || !cdl_4.eq(cdl_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if (!cdl_5.read(tmp_file1, (long)0) || !cdl_5.eq(cdl_3)) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  // close and  delete the temporary files
  //
  tmp_file0.close();
  tmp_file1.close();

  File::remove(tmp_filename0);
  File::remove(tmp_filename1);
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  //--------------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //     extensions to required methods
  //
  //--------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: extensions to required methods...\n");
    Console::increaseIndention();
  }
  
  // create some temporary objects of lenght 3
  //  
  CircularDelayLine<Float> cdl_7(3);
  CircularDelayLine<Float> cdl_8(3);
  CircularDelayLine<Float> cdl_9(3);
  
  cdl_8.assign(cdl_7);
  
  if (!(cdl_8.eq(cdl_7))) {
     return Error::handle(name(), L"assign", Error::TEST,__FILE__, __LINE__);
  }

  cdl_9 = cdl_7;
  
  if (!(cdl_9.eq(cdl_7))) {
     return Error::handle(name(), L"operator=", Error::TEST,__FILE__, __LINE__);
  }

  // test clear
  //
  cdl_9.clear();

  if (cdl_9.length() != 0) {
    return Error::handle(name(), L"clear", Error::TEST, __FILE__, __LINE__);
  }
  
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
   
  //---------------------------------------------------------------------------
  //
  // 3. class-specific public methods:
  //     indexing methods
  //
  //---------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: indexing methods...\n");
    Console::increaseIndention();
  }

  // place 3 items on the buffer
  //
  for (long i = 0; i < 3; i++) {
    Float tmp = i*10;
    cdl_7.assignAndAdvance(tmp);
  }
  
  // retrieve 3 items from the buffer in forward order
  //
  for (long i = 0; i < 3; i++) {
    Float tmp = cdl_7(i);
    Float tmp_1 = i*10;
    if (!tmp_1.eq(tmp)) {
      return Error::handle(name(), L"operator()", Error::TEST,
			   __FILE__, __LINE__);
    }
  }
  
  // clear and do it again with advanceAndAssign
  //
  cdl_7.clear();
  cdl_7.setLength(3);
  
  // place 3 items on the buffer -- do the first assign by hand
  //
  Float tmp = 0;
  cdl_7.assign(tmp);
  for (long i = 1; i < 3; i++) {
    tmp = i*10;
    cdl_7.advanceAndAssign(tmp);
  }


  // do the last advance by hand
  //
  cdl_7.advance(1);
  
  // retrieve 3 items from the buffer in forward order
  //
  for (long i = 0; i < 3; i++) {
    Float tmp = cdl_7(i);
    Float tmp_1 = i*10;
    if (!tmp_1.eq(tmp)) {
      return Error::handle(name(), L"operator()", Error::TEST,
			   __FILE__, __LINE__);
    }
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
    
  //--------------------------------------------------------------------
  //
  // 4. class-specific public methods:
  //     size-related methods
  //
  //--------------------------------------------------------------------
  
  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: size-related methods...\n");
    Console::increaseIndention();
 }

  // set and get the length
  //
  CircularDelayLine<Float> cdl_6;

  cdl_6.setLength((long)4);
  if (cdl_6.length() != 4) {
    return Error::handle(name(), L"set/get Length", Error::TEST,
			 __FILE__, __LINE__);
  }
  // reset indentation
  //
  if (level_a > Integral::NONE) {
   Console::decreaseIndention();
  }

  //--------------------------------------------------------------------
  //
  // 5. class-specific public methods:
  //     core data manipulation methods
  //
  //--------------------------------------------------------------------
  
  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing class-specific public methods: core data manipulation methods...\n");
    Console::increaseIndention();
      }

  // put some data onto the buffer - wrap it once to make sure wrapping works
  //
  Float val;
  for (long i = 0; i < 4; i++) {
    val = i*10;
    cdl_6.assignAndAdvance(val);
  }
  for (long i = 0; i < 4; i++) {
    val = i*10;
    cdl_6.assignAndAdvance(val);
  }

  // retrieve the data from the buffer
  //
  cdl_6.advance(-1);
  long j = 3;
  for (long i = 0; i < 4; i++) {
    if ((float)(j*10) != (float)cdl_6(-i)) {
      return Error::handle(name(), L"assignAndAdvance", CircularDelayLine<TObject>::ERR, __FILE__, __LINE__);
    }  
    j--;
  }
 
  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------
  //
  // 6. print completion message
  //
  //--------------------------------------------------------------------
  
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
    String output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }
  
  // exit gracefully
  //
  return true;
}

// end of include file
//
#endif
