// file: $isip/class/sp/AudioFrontEnd/afnd_02.cc
// version: $Id: afnd_02.cc 9236 2003-06-23 18:12:22Z gao $
//

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

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
boolean AudioFrontEnd::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
  //
  //--------------------------------------------------------------------

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

  AudioFrontEnd aufe;
  AudioFile adf;

  Filename raw_file(L"$ISIP_DEVEL/doc/examples/data/audio/little_endian.raw");

  adf.setFileFormat(AudioFile::RAW);
  adf.setNumChannels(1);
  adf.setAmplitudeRange(0.01);

  aufe.setAudioInput(adf);
  
  // change some parameters of the window
  //
  aufe.setFrameDuration(0.02);

  Float num = aufe.getFrameDuration();
  if (!num.almostEqual((float)0.02)) {
    return Error::handle(name(), L"set/getFrameDuration", Error::TEST,
                         __FILE__, __LINE__);
  }

  // test dynamic constructor
  //
  AudioFrontEnd* dyn_aufe = new AudioFrontEnd(aufe);
  delete dyn_aufe;
  
  // read in a reference
  //
  Sof sof;
  sof.open(L"diagnose_proto_features.sof");
  Vector<VectorFloat> reference;
  
  if (level_a >= Integral::BRIEF) {
    Console::put(L"testing data manipulation methods:\n");
  }

  // test the i/o methods
  //
  AudioFrontEnd val0;
  
  // create temporary 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);
  
  // write the values
  //
  val0.write(tmp_file0, (long)0);
  val0.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 values back in
  //
  AudioFrontEnd val2;
  
  if ((!val2.read(tmp_file0, (long)0)) || (!val0.eq(val2))) {
    return Error::handle(L"fails", L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  if ((!val2.read(tmp_file1, (long)0)) || (!val0.eq(val2))) {
    return Error::handle(L"fails", L"read", Error::TEST, __FILE__, __LINE__);
  }
  
  // close the files
  //
  tmp_file0.close();
  tmp_file1.close();

  Sof recipe_sof;
  recipe_sof.open(L"diagnose_recipe.sof");

  {
    Console::put(L"testing energy\n");
    reference.read(sof, 1);
    AudioFrontEnd aufe0;
    aufe0.read(recipe_sof, 5);

    aufe0.open(raw_file);
    
    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    
    for (; aufe0.getVector(coeffs, 0, f); f++) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"energy");
      }
    }

    aufe0.close();
    
    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }
  }
  
  {
    Console::put(L"testing basic mfcc (no energy)\n");
    AudioFrontEnd aufe0;
    reference.read(sof, 1);
    aufe0.read(recipe_sof, 1);
    AudioFile auf;
    auf.setFileFormat(AudioFile::RAW);
    auf.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(auf);
    
    aufe0.open(raw_file);

    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    boolean res = true;
    
    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }

      VectorFloat ref(12);
      
      if ((f <= (long)509) && ref.move(reference(f), 12, 0, 0) &&
	  (!coeffs.almostEqual(ref))) {
      }
    }
    aufe0.close();
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }

    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  {
    Console::put(L"testing basic mfcc\n");
    AudioFrontEnd aufe0;
    reference.read(sof, 1);

    aufe0.read(recipe_sof, 0);

    AudioFile auf;
    auf.setFileFormat(AudioFile::RAW);
    auf.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(auf);

    aufe0.open(raw_file);

    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    boolean res = true;
    
    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }
    }
    
    aufe0.close();
    
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }

    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }

  }
  
  {
    Console::put(L"testing mfcc with zero mean\n");
    AudioFrontEnd aufe0;
    reference.read(sof, 4);

    Sof sof;
    sof.open(L"diagnose_recipe_zeromean.sof");
    aufe0.read(sof, 0);
    sof.close();

    AudioFile auf;
    auf.setFileFormat(AudioFile::RAW);
    auf.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(auf);
    
    aufe0.open(raw_file);

    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    boolean res = true;
    
    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }
      if (coeffs.length() != 13) {
	return Error::handle(name(), L"getVector", Error::TEST, __FILE__, __LINE__);
      }
      if ((f <= (long)509) && (!coeffs.almostEqual(reference(f)))) {
      }
    }
    aufe0.close();
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }

    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }

  }

  {
    Console::put(L"testing mfcc + delta\n");
    AudioFrontEnd aufe0;
    reference.read(sof, 3);

    aufe0.read(recipe_sof, 2);
    boolean res = true;

    AudioFile auf;
    auf.setFileFormat(AudioFile::RAW);
    auf.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(auf);
    
    aufe0.open(raw_file);
    
    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }

      if (f <= (long)507) {
	VectorFloat ref(reference(f));
	ref.setLength(26);
	if (!coeffs.almostEqual(ref) && (f > (long)1)) {
	}
      }
    }
    aufe0.close();
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }
    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  {
    Console::put(L"testing mfcc + delta + acc\n");
    AudioFrontEnd aufe0;
    AudioFile aufin, aufout;
    aufin.setFileFormat(AudioFile::RAW);
    aufin.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(aufin);
  
    reference.read(sof, 3);
    aufe0.read(recipe_sof, 3);
    
    boolean res = true;

    aufe0.open(raw_file);

    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }

      if ((f <= (long)505) && (f > (long)3) &&
	  (!coeffs.almostEqual(reference(f)))) {
      }
    }
    aufe0.close();
    
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }

    {
      String output;
      output.assign(L"finished frame ");
      output.concat(f);
      output.concat(L", last = ");
      output.concat(aufe0.getLastFrame());
      Console::put(output);
    }
    
    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

  {
    Console::put(L"testing mfcc + delta + acc (nested combination)\n");
    AudioFrontEnd aufe0;
    AudioFile aufin, aufout;
    aufin.setFileFormat(AudioFile::RAW);
    aufin.setFileType(AudioFile::BINARY);
    aufe0.setAudioInput(aufin);
    
    reference.read(sof, 3);
    aufe0.read(recipe_sof, 4);
    
    boolean res = true;

    aufe0.open(raw_file);

    if (aufe0.getID().ne(L"little_endian")) {
      return Error::handle(name(), L"getID", Error::TEST, __FILE__, __LINE__);
    }

    // loop through all vectors of the audio file and output the vector
    //
    VectorFloat coeffs;
    Long f = 0;
    for (; aufe0.getVector(coeffs, 0, f); f = f + (long)1) {
      if ((level_a >= Integral::ALL) && (f < (long)3)) {
	coeffs.debug(L"coeffs");
      }

      if ((f <= (long)505) && (f > (long)3) &&
	  (!coeffs.almostEqual(reference(f)))) {
      }
    }
    aufe0.close();
    
    if (!res) {
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__, __LINE__);
    }

    if (f != (long)512) {
      String output;
      output.assign(L"last frame processed = ");
      output.concat(f);
      Console::put(output);
      return Error::handle(name(), L"getValue", Error::TEST, __FILE__,
			   __LINE__);
    }
  }

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

  //--------------------------------------------------------------------------
  //
  // 2. class-specific public methods:
  //
  //--------------------------------------------------------------------------

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

  // now test through the run interface writing to an AudioFile
  //
  {
    Console::put(L"testing through the run interface -> AudioFile\n");
    AudioFrontEnd reg_fe;
    Filename reg_file(L"$ISIP_DEVEL/doc/examples/data/audio/little_endian.raw");
    // setup the front end
    //
    Sof sof;
    sof.open(L"diagnose_filter.sof");
    reg_fe.read(sof, 0);
    sof.close();

    Filename input(reg_file);
    Filename output;
    Filename output_temp(L"$ISIP_DEVEL/class/sp/AudioFrontend");
    reg_fe.setOutputDirectory(output_temp);

    Console::put(L"running file 0");
    reg_fe.setDebug(Integral::BRIEF);
    reg_fe.run(input);

    Vector<Filename> temp;
    reg_fe.getOutputFilename(temp);
    
    output.assign(temp(0));
    
    if (File::compare(output, input)) {
      return Error::handle(name(), L"run", Error::TEST, __FILE__, __LINE__);
    }

    if (File::size(output) != File::size(reg_file)) {
      return Error::handle(name(), L"run", Error::TEST, __FILE__, __LINE__);
    }
    
    File::remove(output);
    
    input.assign(L"$ISIP_DEVEL/doc/examples/data/audio/bg_119oo39a.raw");
    Console::put(L"running file 1");
    reg_fe.run(input);

    temp.clear();
    reg_fe.getOutputFilename(temp);
    
    output.assign(temp(0));
    
    if (File::compare(output, input)) {
      return Error::handle(name(), L"run", Error::TEST, __FILE__, __LINE__);
    }
    
    if (File::size(output) != File::size(input)) {
      return Error::handle(name(), L"run", Error::TEST, __FILE__, __LINE__);
    }
    
    File::remove(output);
  }

  // reset indentation
  //
  if (level_a > Integral::NONE) {
    Console::decreaseIndention();
  }
  
  // --------------------------------------------------------------------
  //
  // 3. 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;
}
