// file: $isip/class/algo/Window/win_02.cc
// version: $Id: win_02.cc 9259 2003-07-03 20:08:45Z parihar $
//

// isip include files
//
#include "Window.h"
#include <Console.h>

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
boolean Window::diagnose(Integral::DEBUG level_a) {

  //---------------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //---------------------------------------------------------------------------

  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(CLASS_NAME);
    output.concat(L": ");
    Console::put(output);
    Console::increaseIndention();
  }

  //--------------------------------------------------------------------------
  //
  // 1. required public methods
  //     class constructors
  //
  //--------------------------------------------------------------------------

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing required public methods...\n");
    Console::increaseIndention();
  }
  
  Window win0;
  win0.setAlgorithm(RECTANGULAR);
  win0.setNormalization(NONE);
  win0.setDuration(0.025);
  //  win0.init();

  Window win1(win0);
  
  if (!win1.eq(win0)) {
    win0.debug(L"win0");
    win1.debug(L"win1");
    return Error::handle(name(), L"copy constructor", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test large allocation construction and deletion
  //
  if (level_a == Integral::ALL) {
    
    Console::put(L"\ntesting large chunk memory allocation and deletion:\n");
    
    // set the memory to a strange block size so we can hopefully catch any
    // frame overrun errors
    //
    
    Window::setGrowSize((long)500);
    
    Window* pwin = new Window();

    for (long j = 1; j <= 100; j++) {
      Window** pwins = new Window*[j * 100];
      
      // create the objects
      //
    for (long i = 0; i < j * 100; i++) {
      pwins[i] = new Window();
    }
    
    // delete objects
    //
    for (long i = (j * 100) - 1; i >= 0; i--) {
      delete pwins[i];
    }
    
    delete [] pwins;
    } 
    
    delete pwin;
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 2. required public methods
  //     i/o methods 
  //
  //--------------------------------------------------------------------------

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

  // declare a reference object
  //
  win0.setAlgorithm(HAMMING);
  win0.setAlignment(LEFT);
  win0.setNormalization(NONE);
  win0.setDuration(0.0250);
  win0.init();

  // 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);

  win0.write(tmp_file0, (long)0);
  win0.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 object back
  //
  win1.read(tmp_file0, (long)0);
  win1.init();

  if (!win0.eq(win1)) {
    return Error::handle(name(), L"i/o", Error::TEST,
			 __FILE__, __LINE__);
  }
    
  win1.read(tmp_file1, (long)0);
  win1.init();

  if (!win0.eq(win1)) {
    return Error::handle(name(), L"i/o", 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();
  }

  //--------------------------------------------------------------------------
  //
  // 3. class-specific public methods
  //     set and get methods
  //
  //--------------------------------------------------------------------------

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

  // establish an object
  //
  win0.setAlgorithm(HAMMING);
  VectorFloat tmp(L"27.0, 9.0, 3.0");

  // set the parameters manually
  //
  win0.setAlgorithm(RECTANGULAR);
  win0.setAlignment(LEFT);
  win0.setNormalization(UNIT_ENERGY);
  win0.setDuration(2.0);
  win0.setComputeMode(CROSS_FRAME);
  win0.setSize(3);

  // check that the values were set
  //
  if (win0.algorithm_d != RECTANGULAR) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (win0.cmode_d != CROSS_FRAME) {
    return Error::handle(name(), L"setMode", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (win0.alignment_d != LEFT) {
    return Error::handle(name(), L"setAlignment", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (win0.normalization_d != UNIT_ENERGY) {
    return Error::handle(name(), L"setNormalization", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (win0.duration_d != (float)2.0) {
    return Error::handle(name(), L"setDuration", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (win0.data_d.length() != 3) {
    return Error::handle(name(), L"setSize", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test setConstants
  //
  win0.setAlgorithm(BLACKMAN);
  win0.setConstants(tmp);

  if (!win0.constants_d.eq(tmp)) {
    return Error::handle(name(), L"setData", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test setData
  //
  win0.setAlgorithm(CUSTOM);
  win0.setData(tmp);

  if (!win0.data_d.eq(tmp)) {
    return Error::handle(name(), L"setData", Error::TEST,
			 __FILE__, __LINE__);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  //--------------------------------------------------------------------------
  //
  // 4. class-specific public methods
  //     computation methods
  //
  //--------------------------------------------------------------------------

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

  // test the basic compute method:
  //  create a vector, multiplying it by the window, and see if it gives us
  //  back the window.
  //
  VectorFloat input(L"1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2");
  VectorFloat scale(L"1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5, 1, 0.5");
  VectorFloat output;
  VectorFloat ans;

  win0.clear();
  win0.setSize(input.length());
  win0.setAlgorithm(RECTANGULAR);
  win0.setNormalization(NONE);
  win0.setDuration(0.01);
  win0.init();

  ans.assign(win0.getData());
  win0.compute(output, input);
  output.mult(scale);

  if (!ans.almostEqual(output)) {
    ans.debug(L"ans");
    output.debug(L"output");
    win0.debug(L"win0");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  VectorComplexFloat cmplx_input(L"1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j");
  VectorComplexFloat cmplx_ans(L"1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j, 1+1j, 2+2j");
  VectorComplexFloat cmplx_output;
  win0.compute(cmplx_output, cmplx_input);

  if (!cmplx_ans.almostEqual(cmplx_output)) {
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the RECTANGULAR window
  //
  win0.setAlgorithm(RECTANGULAR);
  win0.compute(output, input);
  ans.assign(L"1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the BARTLETT window
  //
  win0.setAlgorithm(BARTLETT);
  win0.compute(output, input);
  ans.assign(L"0.1, 0.389474, 0.289474, 0.768421, 0.478947, 1.14737, 0.668421, 1.52632, 0.857895, 1.90526, 0.952632, 1.71579, 0.763158, 1.33684, 0.573684, 0.957895, 0.38421, 0.578947, 0.194737, 0.2");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the BLACKMAN window
  //
  win0.setAlgorithm(BLACKMAN);
  win0.compute(output, input);
  ans.assign(L"0.84923, 1.05735, 0.226899, 0.111656, 0.00158469, 0.0414436, 0.133271, 0.764762, 0.716524, 1.92852, 0.964259, 1.43305, 0.382381, 0.266541, 0.0207218, 0.00316938, 0.0558281, 0.453799, 0.528675, 1.69846");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the DOLPH_CHEBYSHEV window
  //
  win0.setAlgorithm(DOLPH_CHEBYSHEV);
  win0.compute(output, input);
  ans.assign(L"0.405618, 0.550077, 0.375813, 1.07166, 0.1997, 0.358307, 0.508916, 1.10699, 0.601162, 1.0705, 0.535248, 1.20232, 0.553495, 1.01783, 0.179153, 0.3994, 0.535829, 0.751627, 0.275039, 0.811237");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the GAUSSIAN window
  //
  win0.setAlgorithm(GAUSSIAN);
  win0.compute(output, input);
  ans.assign(L"0.495611, 1.14018, 0.64564, 1.43983, 0.790346, 1.70855, 0.909118, 1.9051, 0.982652, 1.99611, 0.998057, 1.9653, 0.95255, 1.81824, 0.854273, 1.58069, 0.719916, 1.29128, 0.57009, 0.991221");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the HAMMING window
  //
  win0.setAlgorithm(HAMMING);
  win0.compute(output, input);
  ans.assign(L"0.08, 0.209848, 0.176995, 0.576808, 0.427077, 1.15597, 0.72478, 1.7031, 0.944558, 1.98745, 0.993726, 1.88912, 0.85155, 1.44956, 0.577987, 0.854153, 0.288404, 0.353991, 0.104924, 0.16");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the HANNING window
  //
  win0.setAlgorithm(HANNING);
  win0.compute(output, input);
  ans.assign(L"0, 0.0541828, 0.10543, 0.453052, 0.377257, 1.08258, 0.700848, 1.67728, 0.939737, 1.98636, 0.993181, 1.87947, 0.838641, 1.4017, 0.54129, 0.754514, 0.226526, 0.210859, 0.0270914, 0");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the KAISER window
  //
  win0.setAlgorithm(KAISER);
  win0.compute(output, input);
  ans.assign(L"0.370651, 0.937826, 0.566715, 1.32193, 0.748606, 1.65346, 0.892712, 1.88859, 0.979712, 1.99547, 0.997733, 1.95942, 0.944296, 1.78542, 0.826732, 1.49721, 0.660966, 1.13343, 0.468913, 0.741302");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the LIFTER window
  //
  win0.setAlgorithm(LIFTER);
  win0.compute(output, input);
  ans.assign(L" 0, 5.13093, 4.09906, 11.1391, 6.94705, 16.4069, 9.31324, 20.5076, 11.006, 23.1088, 11.888, 24, 11.888, 23.1088, 11.006, 20.5076, 9.31325, 16.4069, 6.94705, 11.1391");
  if (!ans.almostEqual(output)) {
    ans.debug(L"expected result");
    output.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test CUSTOM by comparing it to a known window
  //
  win0.setAlgorithm(HAMMING);
  win0.init();
  VectorFloat vals(win0.getData());
  win0.compute(output, input);

  VectorFloat output_tmp;
  win0.setAlgorithm(CUSTOM);
  win0.setData(vals);
  win0.compute(output_tmp, input);
  
  if (!output.almostEqual(output_tmp)) {
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  // test the compute method for a vector of zeros
  //  
  input.assign(0.0);
  win0.compute(output, input);

  if (!output.almostEqual(input)) {
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);
  }

  
  // test the RECTANGULAR window
  //
  win0.setAlgorithm(RECTANGULAR);
  input.assign(L"1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20");

  VectorFloat output_left, output_right, output_center;
  
  output_left.assign(L"1, 2, 3, 4, 5, 6, 7, 8");
  output_center.assign(L"7, 8, 9, 10, 11, 12, 13, 14");
  output_right.assign(L"13, 14, 15, 16, 17, 18, 19, 20");  
  
  // test the window size less than frame size computation
  //
  win0.setDuration(0.001);
  win0.setFrameDuration(0.0025);
  win0.setComputeMode(CROSS_FRAME);

  CircularBuffer<AlgorithmData> in;
  AlgorithmData data;

  data.makeVectorFloat().assign(input);
  in.append(data);

  win0.init();
  
  win0.setAlignment(LEFT);  
  win0.compute(output_tmp, in);

  if (!output_left.almostEqual(output_tmp)) {
    output_left.debug(L"expected result");
    output_tmp.debug(L"compute result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);    
  }
  
  win0.setAlignment(RIGHT);  
  win0.compute(output_tmp, in);

  if (!output_right.almostEqual(output_tmp)) {
    output_right.debug(L"expected result");
    output_tmp.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);    
  }
  
  win0.setAlignment(CENTER);  
  win0.compute(output_tmp, in);

  if (!output_center.almostEqual(output_tmp)) {
    output_center.debug(L"expected result");
    output_tmp.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);    
  }

  input.assign(L"1, 2, 3, 4, 5, 6, 7, 8");
  win0.setDuration(3.0 / 8000.0);
  win0.setFrameDuration(0.001);
  
  CircularBuffer<AlgorithmData> in2;
  AlgorithmData data2;

  data2.makeVectorFloat().assign(input);
  in2.append(data2);

  win0.init();
  
  win0.setAlignment(CENTER);  
  win0.compute(output_tmp, in2);

  output_center.assign(L"3, 4, 5");
  if (!output_center.almostEqual(output_tmp)) {
    output_center.debug(L"expected result");
    output_tmp.debug(L"computing result");
    return Error::handle(name(), L"compute", Error::TEST,
			 __FILE__, __LINE__);    
  }
  
  // test the pad computation
  //
  win0.setDuration(0.025);
  win0.setFrameDuration(0.01);
  win0.setComputeMode(CROSS_FRAME);
  
  win0.setAlignment(LEFT);
  win0.init();
  if (win0.getLeadingPad() != 2) {
    return Error::handle(name(), L"getLeadingPad", Error::TEST,
			 __FILE__, __LINE__);
  }
  if (win0.getTrailingPad() != 0) {
    return Error::handle(name(), L"getTrailingPad", Error::TEST,
			 __FILE__, __LINE__);
  }

  win0.alignment_d = RIGHT;
  if (win0.getLeadingPad() != 0) {
    return Error::handle(name(), L"getLeadingPad", Error::TEST,
			 __FILE__, __LINE__);
  }
  if (win0.getTrailingPad() != 2) {
    return Error::handle(name(), L"getTrailingPad", Error::TEST,
			 __FILE__, __LINE__);
  }

  win0.alignment_d = CENTER;
  if (win0.getLeadingPad() != 1) {
    return Error::handle(name(), L"getLeadingPad", Error::TEST,
			 __FILE__, __LINE__);
  }
  if (win0.getTrailingPad() != 1) {
    return Error::handle(name(), L"getTrailingPad", Error::TEST,
			 __FILE__, __LINE__);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }

  //--------------------------------------------------------------------------
  //
  // 5. print completion message
  //
  //--------------------------------------------------------------------------

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  if (level_a > Integral::NONE) {
    SysString output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;
}
