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

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

// include files
//
#include <typeinfo>

// isip include files
//
#ifndef ISIP_DOUBLE_LINKED_LIST
#include <DoubleLinkedList.h>
#endif

#ifndef ISIP_STRING
#include <String.h>
#endif

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

// Set: this class builds a collection of unique objects. in other words,
// the difference between a set and a linked list is that two objects
// of the same value will not appear twice in a set. every object must
// have a unique value.
// 
template<class TObject>
class Set : public DoubleLinkedList<TObject> {
  
  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:
  
  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // default values and arguments
  //
  //----------------------------------------
  
  // default values
  //

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

  // 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 SetDiagnose.h in order to avoid issues
  //  related to preprocessing of the diagnose code.
  //
  static const String& name();
  
  // setDebug is inherited from the DoubleLinkedList class
  //

  // other debug methods
  //
  boolean debug(const unichar* message) const;

  // method: destructor
  //
  ~Set() {}

  // method: default constructor
  //
  Set(DstrBase::ALLOCATION alloc = DstrBase::DEF_ALLOCATION) {
    this->alloc_d = alloc;
  }

  // method: copy constructor
  //
  Set(const Set<TObject>& arg) {
    this->alloc_d = arg.alloc_d;
    assign(arg);
  }
  
  // method: assign
  //
  boolean assign(const Set<TObject>& arg) {
    return DoubleLinkedList<TObject>::assign(arg);
  }

  // method: operator=
  //
  Set<TObject>& operator=(const Set<TObject>& arg) {
    assign(arg);
    return *this;
  }

  // equality methods are inherited from the DoubleLinkedList class
  //

  // i/o methods:
  //  sofSize is inherited from the DoubleLinkedList class
  //
  
  // 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;

  // method: readData
  //
  boolean readData(Sof& sof, const String& pname = DoubleLinkedList<TObject>::DEF_PARAM,
		   long size = SofParser::FULL_OBJECT,
		   boolean param = true, boolean nested = false) {
    return DoubleLinkedList<TObject>::readData(sof, pname, size, param,
					       nested);
  }

  // method: writeData
  //
  boolean writeData(Sof& sof, const String& pname = DoubleLinkedList<TObject>::DEF_PARAM) const {
    return DoubleLinkedList<TObject>::writeData(sof, pname);
  }

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

  // other memory management methods
  //  clear is inherited from the DoubleLinkedList class

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  overloaded operators and extensions to required methods
  //
  //---------------------------------------------------------------------------

  // method: operator()
  //
  const TObject& operator() (long index) const {
    return (*((DoubleLinkedList<TObject>*)this))(index);
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  access methods. note that only the const versions are allowed.
  //
  //---------------------------------------------------------------------------
  
  // method: getFirst
  //
  const TObject* getFirst() const {
    return DoubleLinkedList<TObject>::getFirst();
  }

  // method: getLast
  //
  const TObject* getLast() const {
    return DoubleLinkedList<TObject>::getLast();
  }
  
  // method: getNext
  //
  const TObject* getNext() const {
    return DoubleLinkedList<TObject>::getNext();
  }
  
  // method: getPrev
  //
  const TObject* getPrev() const {
    return DoubleLinkedList<TObject>::getPrev();
  }
  
  // method: getMark
  //
  const TObject* getMark() const {
    return DoubleLinkedList<TObject>::getMark();
  }

  // method: getCurr
  //
  const TObject* getCurr() const {
    return DoubleLinkedList<TObject>::getCurr();
  }

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  insert and remove methods
  //
  //---------------------------------------------------------------------------
  
  // insert and remove methods:
  //  the current pointer is moved to the node inserted. 
  //
  boolean insert(TObject* item);

  // remove methods are inherited from the list class
  //

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  property methods
  //
  //---------------------------------------------------------------------------
  
  // these methods take advantage of the list being in sorted order
  //
  boolean find(const TObject* item);
  boolean contains(const TObject* item) const;

  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:

  // block these list methods from being used in Set
  //
  boolean sort(Integral::ORDER sort_order = Integral::ASCENDING,
	       DstrBase::SORT_ALGO = DstrBase::DEF_SORT_ALGO);  

  boolean reverse();
  boolean swap(long i, long j);
  boolean insertFirst(TObject* item);
  boolean insertFirst(Set<TObject>& item_list);
  boolean insertLast(TObject* item);
  boolean insertLast(Set<TObject>& item_list);
  boolean apply(boolean (TObject::*method)());
  boolean apply(boolean (TObject::*method)(), Set<TObject>& arg);

  // friend class
  //
  template <class TObject_diagnose> 
  friend class SetDiagnose;  
};

//-----------------------------------------------------------------------------
//
// 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 TObject>
const String Set<TObject>::CLASS_NAME(L"Set");

template <class TObject>
MemoryManager Set<TObject>::mgr_d(sizeof(Set<TObject>), CLASS_NAME);

// below are all the methods for the Set 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 TObject>
const String& Set<TObject>::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(TObject::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 TObject>
boolean Set<TObject>::debug(const unichar* message_a) const {

  // declare local variables
  //
  String output;
  String value;
  String extras;
  
  // print out the debug level
  //
  value.assign(DoubleLinkedList<TObject>::debug_level_d);
  output.debugStr(name(), message_a, L"debug_level_d", value);
  Console::put(output);
  
  // print out the list
  //
  output.debugStr(name(), message_a, L"list");
  Console::put(output);
  DoubleLinkedList<TObject>::debug(L"");
  
  // 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 TObject>
boolean Set<TObject>::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 TObject>
boolean Set<TObject>::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 = this->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);
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  insert methods
//
//---------------------------------------------------------------------------

// method: insert
//
// arguments:
//  TObject* item: (input) the item to be added
//  
// return: a boolean value indicating status
//
// this method adds a new node containing the item after the current position
// of this list
//
template<class TObject>
boolean Set<TObject>::insert(TObject* item_a) {
  
  // make sure the item is not NULL
  //
  if (item_a == (TObject*)NULL) {
    return Error::handle(name(), L"insert", Error::NULL_ARG,
			 __FILE__, __LINE__);
  }

  // the list was empty, add the input item to the beginning
  //
  if (this->isEmpty()) {
    return DoubleLinkedList<TObject>::insertFirst(item_a);
  }
  
  // call the find method. it will return true if the item already
  // exists in the list, in which case we do not insert the item and
  // return false.
  //
  if (find(item_a)) {
    return false;
  }

  // special case: if the item is less than the first item, call
  // insertFirst.
  //
  if (item_a->lt(*DoubleLinkedList<TObject>::getFirst())) {
    return DoubleLinkedList<TObject>::insertFirst(item_a);
  }
  
  // if find returns false it means that the item was not found in the
  // list. the find method will leave the list positioned to the
  // correct place to insert the new node, so just insert
  //
  return DoubleLinkedList<TObject>::insert(item_a);
}

//---------------------------------------------------------------------------
//
// class-specific public methods:
//  property methods
//
//---------------------------------------------------------------------------

// method: contains
//
// arguments:
//  TObject* item: (input) the item to be found
//
// return: a boolean value indicating status
//
// this method determines if an item is contained in this list. it is
// unchanged from the DoubleLinkedList version, the only reason it is
// defined here is so the faster find method can be used.
//
template<class TObject>
boolean Set<TObject>::contains(const TObject* item_a) const {

  // check if the input item is NULL
  //
  if (item_a == (TObject*)NULL) {
    return Error::handle(name(), L"contains", Error::NULL_ARG,
			 __FILE__, __LINE__);
  }

  // save the current position
  //
  DoubleLinkedNode<TObject>* temp_curr = this->curr_d;

  // call the find method
  //
  boolean item_found = const_cast<Set<TObject>*>(this)->find(item_a);

  // revert the current pointer so as to leave the list unchanged
  //
  const_cast<Set<TObject>*>(this)->curr_d = temp_curr;
  
  // return whether or not the item was found
  //
  return item_found;
}

// method: find
//
// arguments:
//  TObject* item: (input) the item to be found
//
// return: a boolean value indicating status
//
// this method finds the first item in the list which is equivalent to
// the item passed in. if no equivalent item is found, false is
// returned. if an item is found, the list is positioned to that
// point, otherwise the list is left in a place where the new item can
// be inserted into the sorted order.
//
template<class TObject>
boolean Set<TObject>::find(const TObject* item_a) {
  
  // check if the input item is NULL
  //
  if (item_a == (TObject*)NULL) {
    return Error::handle(name(), L"find", Error::NULL_ARG,
			 __FILE__, __LINE__);
  }

  // find the item
  //
  for (boolean more = this->gotoFirst(); more; more = this->gotoNext()) {
    if (item_a->eq(*getCurr())) {
      return true;
    }
    else if (item_a->lt(*getCurr())) {
      this->gotoPrev();
      return false;
    }
  }
  
  // return false, it was not found.
  //
  return false;
}

// end of include file
//
#endif
