// file: $isip/class/algo/Output/out_02.cc
// version: $Id: out_02.cc 10633 2006-10-02 18:48:26Z ss754 $
//

// isip include files
//
#include "Output.h"
#include <Console.h>
#include <Filename.h>

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

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

  // output the class name
  //
  if (level_a > Integral::NONE) {
    String 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();
  }

  // test destructor/constructor(s) and memory management
  //
  Output const0;
  const0.setAlgorithm(DATA);
  const0.setImplementation(SAMPLED_DATA);

  Output const1(const0);
  
  if (!const1.eq(const0)) {
    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
    //
    Output::setGrowSize((long)500);
    
    Output* pconst = new Output();

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

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

  const0.setAlgorithm(DATA);
  const0.setImplementation(SAMPLED_DATA);

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


  const0.write(tmp_file0, (long)0);
  //  const0.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
  //
  const1.read(tmp_file0, (long)0);
  const1.init();

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

  if (!const0.eq(const1)) {
    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
  //
  const0.setAlgorithm(DATA);
  const0.setImplementation(SAMPLED_DATA);

  // check that the values were set
  //
  if (const0.algorithm_d != DATA) {
    return Error::handle(name(), L"setAlgorithm", Error::TEST,
			 __FILE__, __LINE__);
  }
  else if (const0.implementation_d != SAMPLED_DATA) {
    return Error::handle(name(), L"setImplementation", 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 write to a file
  //

  // set indentation
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out to a file...\n");
    Console::increaseIndention();
  }
  
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a RAW TEXT FEATURES file...\n");
  }

  String tmp_diagnose;
  Integral::makeTemp(tmp_diagnose);
  
  Output c1;
  c1.setSignalDuration(0.01);
  
  
  c1.setInputFilename(tmp_diagnose);
  c1.setOutputExtension(L"ftr_text_raw");
  c1.setFileFormat(FeatureFile::RAW);
    
  Vector < CircularBuffer < AlgorithmData> > in;
  Vector < AlgorithmData> out;
  
  Vector<VectorFloat> result;
  result.setLength(4);
  result(0).assign(L"1, 3, 5, 7, 9");
  result(1).assign(L"10, 30, 50, 70, 90");  
  result(2).assign(L"100, 300, 500, 700, 900");
  result(3).assign(L"1000, 3000, 5000, 7000, 9000");
  
  long N = 4;
  in.setLength(N);
  
  for (long i = 0; i < N; i++) {
    in(i)(0).makeVectorFloat();
    in(i)(0).getVectorFloat().assign(result(i));
    in(i)(0).setCoefType(AlgorithmData::SIGNAL);
  }

  // multip-channel and VectorFloat test
  //
  if (!c1.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }
  
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a RAW BINARY FEATURES file...\n");
  }
  // test binary format
  //
  c1.clear();
  c1.setSignalDuration(0.01);
  c1.setInputFilename(tmp_diagnose);
  c1.setOutputExtension(L"ftr_bin_raw");    
  c1.setFileFormat(FeatureFile::RAW);    
  c1.setFileType(FeatureFile::BINARY);
  
  if (!c1.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }
  
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a SOF TEXT FEATURES file...\n");
  }

  Vector < CircularBuffer < AlgorithmData> > in2;

  Vector<MatrixFloat> result2;
  result2.setLength(4);
  result2(0).assign(2, 2, L"1, 3, 5, 7");
  result2(1).assign(2, 2, L"10, 30, 50, 70");  
  result2(2).assign(3, 2, L"100, 300, 500, 700, 900, 1100");
  result2(3).assign(2, 2, L"1000, 3000, 5000, 7000");
  
  long N2 = 4;
  in2.setLength(N2);
  
  for (long i = 0; i < N2; i++) {
    in2(i)(0).makeMatrixFloat();
    in2(i)(0).getMatrixFloat().assign(result2(i));
    in2(i)(0).setCoefType(AlgorithmData::SIGNAL);
  }

  c1.clear();
  c1.setSignalDuration(0.01);
  c1.setInputFilename(tmp_diagnose);
  c1.setOutputExtension(L"ftr_text_sof");
  c1.setFileFormat(FeatureFile::SOF);
  c1.setFileType(FeatureFile::TEXT);

  if (!c1.apply(out, in2)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a SOF BINARY FEATURES file...\n");
  }

  // test binary format
  //
  c1.clear();
  c1.setSignalDuration(0.01);
  c1.setInputFilename(tmp_diagnose);
  c1.setOutputExtension(L"ftr_bin_sof");    
  c1.setFileType(FeatureFile::BINARY);
  c1.setFileFormat(FeatureFile::SOF);    

  if (!c1.apply(out, in2)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  // Test SAMPLED_DATA output
  //
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a RAW BINARY SAMPLED_DATA file...\n");
  }
  
  Output c2;
  c2.setSignalDuration(0.01);
  c2.setInputFilename(tmp_diagnose);
  c2.setImplementation(SAMPLED_DATA);
  c2.setFileFormat(AudioFile::RAW);
  c2.setFileType(AudioFile::BINARY);
  c2.setOutputExtension(L"sampled_bin_raw");    
  
  if (!c2.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a RAW TEXT SAMPLED_DATA file...\n");
  }
    
  c2.clear();
  c2.setSignalDuration(0.01);
  c2.setInputFilename(tmp_diagnose);  
  c2.setOutputExtension(L"sampled_text_raw");    
  c2.setFileType(AudioFile::TEXT);
  c2.setImplementation(SAMPLED_DATA);
  c2.setFileFormat(AudioFile::RAW);
  c2.setFileType(AudioFile::TEXT);

  if (!c2.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }
  
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a SOF BINARY SAMPLED_DATA file...\n");
  }
  
  c2.clear();
  c2.setSignalDuration(0.01);
  c2.setInputFilename(tmp_diagnose);  
  c2.setOutputExtension(L"sampled_bin_sof");
  c2.setImplementation(SAMPLED_DATA);
  
  c2.setFileType(AudioFile::BINARY);
  c2.setFileFormat(AudioFile::SOF);
  
  if (!c2.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }
  
  if (level_a > Integral::NONE) {
    Console::put(L"testing writing out a SOF TEXT SAMPLED_DATA file...\n");
  }
  
  c2.clear();
  c2.setSignalDuration(0.01);
  c2.setInputFilename(tmp_diagnose);
  c2.setOutputExtension(L"sampled_text_sof");
  c2.setImplementation(SAMPLED_DATA);
  
  c2.setFileType(AudioFile::TEXT);
  c2.setFileFormat(AudioFile::SOF);
  
  if (!c2.apply(out, in)) {
    return Error::handle(name(), L"diagnose", Error::TEST, __FILE__, __LINE__);
  }

  // register file in order to remove the temp file, this is slightly
  // ugly code because we add extension for each file
  //
  String temp;
  temp.assign(tmp_diagnose);
  tmp_diagnose.concat(L".ftr_text_raw");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".ftr_bin_raw");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".ftr_text_sof");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".ftr_bin_sof");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".sampled_bin_raw");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".sampled_text_raw");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".sampled_bin_sof");
  File::registerTemp(tmp_diagnose);

  tmp_diagnose.assign(temp);
  tmp_diagnose.concat(L".sampled_text_sof");
  File::registerTemp(tmp_diagnose);

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  // 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) {
    String output(L"diagnostics passed for class ");
    output.concat(name());
    output.concat(L"\n");
    Console::put(output);
  }

  // exit gracefully
  //
  return true;
}
