// file: $isip/class/dstr/Triple/Triple.h
// version: $Id: Triple.h 5965 2000-12-17 23:28:40Z picone $
//

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

// isip include files
//
#ifndef ISIP_STRING
#include <String.h>
#endif

#ifndef ISIP_CHAR
#include <Char.h>
#endif

#ifndef ISIP_FLOAT
#include <Float.h>
#endif

#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

#ifndef ISIP_CONSOLE	
#include <Console.h>
#endif

// Triple: a generic Triple template class. this is simply a container
// class that groups together three arbitrary objects.
//
template<class T1, class T2, class T3>
class Triple {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;

  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  static const String DEF_PARAM;
  static const String BLOCK_START_STR;
  static const String BLOCK_DELIM_STR;
  static const String BLOCK_END_STR;
  static const String BLOCK_TERM_STR;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------

  // default values
  //
    
  // default arguments to methods
  //
  
  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  
  
  static const long ERR = 41900;
  
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // define an object of each type
  //
  T1 obj1_d;
  T2 obj2_d;
  T3 obj3_d;

  // define the memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // static methods:
  //  diagnose method is moved outside the class header file and
  //  defined in the TripleDiagnose.h in order to avoid issues
  //  related to preprocessing of the diagnose code.
  //
  static const String& name();
  
  // method: setDebug
  //
  static boolean setDebug(Integral::DEBUG debug_level) {
    T1::setDebug(debug_level);
    T2::setDebug(debug_level);
    T3::setDebug(debug_level);
    return true;
  }

  // other debug methods
  //
  boolean debug(const unichar* message) const;
  
  // method: destructor
  //  
  ~Triple() {}

  // method: default constructor
  //  
  Triple() {}

  // method: copy constructor
  //  
  Triple(const Triple<T1,T2,T3>& arg) {
    assign(arg);
  }
  
  // method: assign
  //
  boolean assign(const Triple<T1,T2,T3>& arg) {
   return (obj1_d.assign(arg.obj1_d) && obj2_d.assign(arg.obj2_d)
	   && obj3_d.assign(arg.obj3_d));
  }

  // method: operator=
  //
  Triple<T1,T2,T3>& operator=(const Triple<T1,T2,T3>& arg) {
    assign(arg);
    return *this;
  }

  // method: eq
  //  determines if the contents of the Triple are equivalent.  this
  //  method calls the eq(TObject&) method for the items contained in
  //  the respective nodes to determine equivalence.  only classes
  //  with an eq() method are available for use with this Triple
  //  object
  //
  boolean eq(const Triple<T1,T2,T3>& arg) const {
    return (obj1_d.eq(arg.obj1_d) && obj2_d.eq(arg.obj2_d)
	    && obj3_d.eq(arg.obj3_d));    
  }    
  
  // method: sofSize  
  //
  long sofSize() const {
    return obj1_d.sofSize() + obj2_d.sofSize() + obj3_d.sofSize();
  }

  // method: read
  //
  boolean read(Sof& sof, long tag) {
    return read(sof, tag, name());
  }

  // method: write
  //
  boolean write(Sof& sof, long tag) const {
    return write(sof, tag, name());
  }

  // other i/o methods
  //
  boolean read(Sof& sof, long tag, const String& name);
  boolean write(Sof& sof, long tag, const String& name) const;
  
  boolean readData(Sof& sof, const String& pname = DEF_PARAM, long size =
                   SofParser::FULL_OBJECT, boolean param = true,
                   boolean nested = false);
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) const;
  
  // method: new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

  // method: new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }

  // method: delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }

  // method: delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }

  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }

  // method: clear
  //   
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE) {
    return (obj1_d.clear(cmode) && obj2_d.clear(cmode) && obj3_d.clear(cmode));
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  extensions to required method  
  //
  //---------------------------------------------------------------------------

  // method: constructor
  //
  Triple(const T1& obj1, const T2& obj2, const T3& obj3) {
    assign(obj1, obj2, obj3);
  }

  // method: assign
  //  
  boolean assign(const T1& obj1, const T2& obj2, const T3& obj3) {
    return (obj1_d.assign(obj1) && obj2_d.assign(obj2)&& obj3_d.assign(obj3));
  }    

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  item access methods
  //
  //--------------------------------------------------------------------------

  // method: first
  //  
  T1& first() {
    return obj1_d;
  }

  // method: first
  //  
  const T1& first() const {
    return obj1_d;
  }

  // method: second
  //  
  T2& second() {
    return obj2_d;
  }

  // method: second
  //  
  const T2& second() const {
    return obj2_d;
  }

  // method: third
  //
  T3& third() {
    return obj3_d;
  }    
  
  // method: third
  //
  const T3& third() const {
    return obj3_d;
  }    
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

}; 

//-----------------------------------------------------------------------------
//
// we define non-integral constants at the end of class definition for
// templates (for non-templates these are defined in the default constructor)
//      
//-----------------------------------------------------------------------------

// constants: required constants such as the class name
//
template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::CLASS_NAME(L"Triple");

// constants: required constants for i/o methods
//
template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::DEF_PARAM(L"Triple");

template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::BLOCK_START_STR(L"{");

template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::BLOCK_DELIM_STR(L"}, {");

template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::BLOCK_END_STR(L"}");

template <class T1, class T2, class T3>
const String Triple<T1, T2, T3>::BLOCK_TERM_STR(L";\n");

// static instantiations: debug level and memory manager
//
template <class T1, class T2, class T3>
MemoryManager Triple<T1, T2, T3>::mgr_d(sizeof(Triple<T1, T2, T3>), CLASS_NAME);

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

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

// method: name
//
// arguments: none
//
// return: a static String& containing the class name
//
// this method returns the class name
//
template<class T1, class T2, class T3>
const String& Triple<T1, T2, T3>::name() {

  // create the static name string for this class and return it
  //
  static String cname(CLASS_NAME);
  cname.clear();
  cname.concat(CLASS_NAME);
  cname.concat(L"<");
  cname.concat(T1::name());
  cname.concat(L",");
  cname.concat(T2::name());
  cname.concat(L",");
  cname.concat(T3::name());
  cname.concat(L">");

  // return the name
  //
  return cname;
}

//----------------------------------------------------------------------
//
// required debug methods
//
//----------------------------------------------------------------------

// method: debug
//
// arguments:
//  const unichar* message: (input) information message
//
// return: a boolean value indicating status
//
// this method dumps the contents of an object to the console
// 
template<class T1, class T2, class T3>
boolean Triple<T1, T2, T3>::debug(const unichar* message_a) const {

  // local variables
  //
  String output;

  // output the user's message
  //
  output.debugStr(name(), message_a, L"");
  Console::put(output);

  // increment the indention level in the console
  //
  Console::increaseIndention();
  
  // call the debug method of the item
  //
  obj1_d.debug(L"first");
  obj2_d.debug(L"second");
  obj3_d.debug(L"third");
  
  // decrement the indention level in the console
  //
  Console::decreaseIndention();
  
  // exit gracefully
  // 
  return true;
}

//------------------------------------------------------------------------
//
// required i/o methods
//
//------------------------------------------------------------------------

// method: read
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file
//
template<class T1, class T2, class T3>
boolean Triple<T1, T2, T3>::read(Sof& sof_a, long tag_a, const String& name_a) {
  
  // get the instance of the object from the Sof file
  //
  if (!sof_a.find(name_a, tag_a)) {
    return false;
  }

  // read the actual data from the sof file
  //
  if (!readData(sof_a)) {
    return false;
  }

  // exit gracefully
  //
  return true;
}

// method: write
//
// arguments:
//  Sof& sof: (input) sof file object
//  long tag: (input) sof object instance tag
//  const String& name: (input) sof object instance name
//
// return: a boolean value indicating status
//
// this method has the object write itself to an Sof file
//
template<class T1, class T2, class T3>
boolean Triple<T1, T2, T3>::write(Sof& sof_a, long tag_a, const String& name_a)
  const {

  // declare a temporary size variable
  //
  long obj_size = 0;

  // switch on ascii or binary mode
  //
  if (sof_a.isText()) {

    // set the size to be dynamic
    //
    obj_size = Sof::ANY_SIZE;
  }
  else {

    // the size of the binary data to write
    //
    obj_size = sofSize();
  }
  
  // put the object into the sof file's index
  //
  if (!sof_a.put(name_a, tag_a, obj_size)) {
    return false;
  }
  
  // exit gracefully
  //
  return writeData(sof_a);
}

// method: readData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//  long size: (input) size of the object
//  boolean param: (input) is the parameter specified?
//  boolean nested: (input) is this nested?
//
// return: a boolean value indicating status
//
// this method has the object read itself from an Sof file. it assumes
// that the Sof file is already positioned correctly.
//
template<class T1, class T2, class T3>
boolean Triple<T1, T2, T3>::readData(Sof& sof_a, const String& pname_a,
				   long size_a,
				   boolean param_a, boolean nested_a) {
  
  // local variables
  //
  SofParser parser;
  String pname;
  
  // if param is false, this means implicit parameter
  //
  if (!param_a) {
    parser.setImplicitParam();
    pname.assign(parser.implicitPname());
  }
  else {
    pname.assign(pname_a);
  }
  
  // configure the parser to read a nested object
  //
  if (nested_a) {
    parser.setNest();
  }
  
  // load the parse
  //
  if (!parser.load(sof_a, size_a)) {
    return Error::handle(name(), L"readData", Error::READ,
			 __FILE__, __LINE__, Error::WARNING);
  }
  
  // read the length first: this differs for text or binary
  //
  if (sof_a.isText()) {
    long new_size = parser.countTokens(pname);
    if (new_size != 3) {
      return Error::handle(name(), L"readData", Error::READ, __FILE__,
			   __LINE__, Error::WARNING);
    }
  }

  if (!obj1_d.readData(sof_a, pname, parser.getEntry(sof_a, pname, 0, 1),
		       false, true)) {
    return Error::handle(name(), L"readData", Error::READ, __FILE__,
			 __LINE__, Error::WARNING);
  }

  if (!obj2_d.readData(sof_a, pname, parser.getEntry(sof_a, pname, 1, 1),
		       false, true)) {
    return Error::handle(name(), L"readData", Error::READ, __FILE__,
			 __LINE__, Error::WARNING);
  }

  if (!obj3_d.readData(sof_a, pname, parser.getEntry(sof_a, pname, 2, 1),
		       false, true)) {
    return Error::handle(name(), L"readData", Error::READ, __FILE__,
			 __LINE__, Error::WARNING);
  }

  // exit gracefully
  //
  return true;
}

// method: writeData
//
// arguments:
//  Sof& sof: (input) sof file object
//  const String& pname: (input) parameter name
//
// return: a boolean value indicating status
//
// this method writes the object to the Sof file. it assumes that the
// Sof file is already positioned correctly.
//
template<class T1, class T2, class T3>
boolean Triple<T1, T2, T3>::writeData(Sof& sof_a, const String& pname_a) const {

  // if text, write a parameter name. this can't be done with
  // writeLabelPrefix because an empty list gets no brackets
  //
  if (sof_a.isText()) {

    if (pname_a.length() > 0) {
      String output;
      output.assign(pname_a);
      output.concat(SofParser::SPACE_CHAR);
      output.concat(SofParser::DEF_ASSIGNMENT_CHAR);
      output.concat(SofParser::SPACE_CHAR);
      sof_a.puts(output);
    }

    sof_a.puts(BLOCK_START_STR);
  }

  // write the first element
  //
  if (!obj1_d.writeData(sof_a, String::getEmptyString())) {
    return Error::handle(name(), L"writeData",Error::IO, __FILE__, __LINE__);
  }

  if (sof_a.isText()) {
    sof_a.puts(BLOCK_DELIM_STR);
  }
  
  // write the second element
  //
  if (!obj2_d.writeData(sof_a, String::getEmptyString())) {
    return Error::handle(name(), L"writeData",Error::IO, __FILE__, __LINE__);
  }

  if (sof_a.isText()) {
    sof_a.puts(BLOCK_DELIM_STR);
  }

  // write the third element
  //
  if (!obj3_d.writeData(sof_a, String::getEmptyString())) {
    return Error::handle(name(), L"writeData",Error::IO, __FILE__, __LINE__);
  }
  
  if (sof_a.isText()) {
    
    sof_a.puts(BLOCK_END_STR);
    
    // possibly terminate the statement
    //
    if (pname_a.length() > 0) {
      sof_a.puts(BLOCK_TERM_STR);
    }
  }

  // exit gracefully
  //
  return true;
}
 
// end of include file
//
#endif
