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

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

// isip include files
//
#ifndef ISIP_TRIPLE
#include <Triple.h>
#endif

#ifndef ISIP_PAIR
#include <Pair.h>
#endif

// TripleDiagnose: a class that contains the diagnose method of Triple class.
//
template<class T1, class T2, class T3>
class TripleDiagnose : public Triple<T1, T2, T3> {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  
  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // default values
  //
    
  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // method: name
  //
  static const String& name() {
    return Triple<T1, T2, T3>::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 TripleDiagnose template class
//

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

// method: diagnose
//
// arguments:
//  Integral::DEBUG level: (input) debug level for diagnostics
//
// return: a boolean value indicating status
//
template<class T1, class T2, class T3>
boolean TripleDiagnose<T1,T2,T3>::diagnose(Integral::DEBUG level_a) {
  
  //---------------------------------------------------------------------------
  //
  // 0. preliminaries
  //
  //---------------------------------------------------------------------------

  // output the class name
  //
  if (level_a > Integral::NONE) {
    SysString output(L"diagnosing class ");
    output.concat(Triple<T1, T2, T3>::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();
  }

  // test setDebug
  //
  Triple<T1, T2, T3>::setDebug(Integral::NONE);
  
  // check the constructors and destructors for allocating on the dynamic
  // memory heap:
  //  default constructor
  //
  Triple<T1, T2, T3>* def_dyn_node = new Triple<T1, T2, T3>();

  // copy constructor
  //
  Triple<T1, T2, T3>* copy_dyn_node = new Triple<T1, T2, T3>(*def_dyn_node);
  
  // see if we can dynamically delete
  //
  delete def_dyn_node;
  delete copy_dyn_node;
  
  // test large allocation construction and deletion
  //  set the memory to a strange block size so we can hopefully catch
  //  any frame overrun errors  
  //
  Triple<T1, T2, T3>::setGrowSize((long)731);
  
  // loop for a large number of times creating and deleting a large number
  // of triples at each loop
  //
  for (long j = 1; j <= 10; j++) {
    Triple<T1, T2, T3>** triples = new Triple<T1, T2, T3>*[j * 10];
    
    // create the triples
    //
    for (long i = 0; i < j * 10; i++) {
      triples[i] = new Triple<T1, T2, T3>;
    }
    
    // delete triples
    //
    for (long i = (j * 10) - 1; i >= 0; i--) {
      delete triples[i];
    }
    
    delete [] triples;
  }
  
  // perform the same test using the new[] and delete [] operators
  //
  for (long j = 1; j <= 10; j++) {
    
    // allocate a large number of triples
    //
    Triple<T1, T2, T3>* triples = new Triple<T1, T2, T3>[j * 10];
    
    // clean up memory
    //
    delete [] triples;
  }

  // test i/o methods: 
  //  we need binary and text sof files
  //
  String text_filename;
  Integral::makeTemp(text_filename);
  String bin_filename;
  Integral::makeTemp(bin_filename);
  
  // open files in write mode
  //
  Sof text_file;
  text_file.open(text_filename, File::WRITE_ONLY, File::TEXT);
  Sof bin_file;
  bin_file.open(bin_filename, File::WRITE_ONLY, File::BINARY);
  
  // create some objects to write
  //
  Char write_char(L'a');
  String write_str(L"aye");
  Float write_flt = (float)1.0;
  Triple<Char, String, Float> write_Triple_node(write_char,
					      write_str, write_flt);

  // print the triples
  //
  if (level_a >= Integral::ALL) {
    write_Triple_node.debug(L"write_Triple_node");
  }
  
  // write the values
  //
  write_Triple_node.write(text_file, (long)0);
  write_Triple_node.write(bin_file, (long)0);  
  
  // close the files
  //
  text_file.close();
  bin_file.close();
  
  // open the files in read mode
  //
  text_file.open(text_filename);
  bin_file.open(bin_filename);

  // create objects for reading in
  //
  Triple<Char, String, Float> read_Triple_node_text;
  Triple<Char, String, Float> read_Triple_node_bin;

  // read in the triples and test for equivalence
  //
  if (!read_Triple_node_text.read(text_file, (long)0) ||
      (!read_Triple_node_text.eq(write_Triple_node))) {
    return Error::handle(name(), L"read", Error::TEST, __FILE__, __LINE__);
  }

  if (!read_Triple_node_bin.read(bin_file, (long)0) ||
      (!read_Triple_node_bin.eq(write_Triple_node))) {
    return Error::handle(name(), L"write", Error::TEST, __FILE__, __LINE__);
  }

  // print the triple out
  //
  if (level_a >= Integral::ALL) {
    read_Triple_node_text.debug(L"read_Triple_node_text");
    read_Triple_node_bin.debug(L"read_Triple_node_bin");
  }
  
  // close and delete the temporary files
  //
  text_file.close();
  bin_file.close();
  File::remove(text_filename);
  File::remove(bin_filename);

  // test equality methods
  //
  Char obj1(L'a');
  String obj2(L"boy");
  Float obj3 = (float)1.0;
  Triple<Char, String, Float> obj_1(obj1, obj2, obj3);
  Triple<Char, String, Float> obj_2(obj1, obj2, obj3);

  if (!obj_1.eq(obj_2)) {
    return Error::handle(name(), L"eq", Error::TEST, __FILE__, __LINE__);
  }

  // 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 and items
  //  
  T1 item0;
  T2 item1;
  T3 item2;
  
  Triple<T1, T2, T3> tmp_node;
  tmp_node.assign(item0, item1, item2);

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

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

  // test first, second and third methods
  //
  Char ret_obj1 = obj_1.first();
  String ret_obj2 = obj_1.second();
  Float ret_obj3 = obj_1.third();

  if (!obj1.eq(ret_obj1)) {
    return Error::handle(name(), L"first", Error::TEST, __FILE__, __LINE__);
  }

  if (!obj2.eq(ret_obj2)) {
    return Error::handle(name(), L"second", Error::TEST, __FILE__, __LINE__);
  }

  if (!obj3.eq(ret_obj3)) {
    return Error::handle(name(), L"third", Error::TEST, __FILE__, __LINE__);
  }
  
  // we want to keep up with characters and associated strings. we
  // could do this with parallel vectors, but combining the inner
  // data is more natural.
  //
  Vector< Triple<Char, String, Float> > letters(5);

  letters(0).first().assign(L'a');
  letters(1).first().assign(L'b');
  letters(2).first().assign(L'c');
  letters(3).first().assign(L'd');
  letters(4).first().assign(L'e');
  
  letters(0).second().assign(L"aye");
  letters(1).second().assign(L"be");
  letters(2).second().assign(L"see");
  letters(3).second().assign(L"de");
  letters(4).second().assign(L"e");
  
  letters(0).third().assign((float)1);
  letters(1).third().assign((float)2);
  letters(2).third().assign((float)3);
  letters(3).third().assign((float)4);
  letters(4).third().assign((float)5);
  
  // we might also want to weight these, so,
  //
  Vector <Pair < Long, Triple <Char, String, Float> > > wlet(5);
  
  for (long i = 0; i < 5; i++) {
    wlet(i).first().assign(i * 3);
    wlet(i).second().assign(letters(i));
  }

  // test the clear method
  //
  wlet(0).clear();
  if (((long)(wlet(0).first()) != 0)
      || (wlet(0).second().first() != (unichar)0)
      || (wlet(0).second().second().ne(L""))
      || (float)(wlet(0).second().third()) != 0.0) {
    return Error::handle(name(), L"clear", Error::TEST, __FILE__, __LINE__);
  }


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

// end of include file
//
#endif
