/*
 * Doubly-linked list classes and templates
 * Header file
 * $Id: dlist.h,v 1.2 2003/04/12 23:44:28 hsteoh Exp hsteoh $
 * ----------------------------------------------------------------------------
 * DESIGN
 *
 * The implementation of doubly-linked list is based on a careful design of
 * how to cleanly implement C++ containers (ref stack.h). This design basically
 * separates the `essential' aspect of the container (the base classes) from
 * the user-customized aspect (the templates which allow arbitrary data types
 * to be stored), called `flavors' of the container. The base classes should
 * ONLY be used for either making new `flavors' (ie. templates) or for deriving
 * other containers by extending this container. The user should ONLY use
 * the `flavors' (templates).
 *
 * Naming conventions: each flavor has a letter associated with it (currently
 * `e' and `o'). The template of that flavor is named by prefixing the flavor
 * letter to the base name of the container (`dlist' in this case). Therefore,
 * the two flavors provided are `edlist' and `odlist'. (This convention applies
 * to all containers based on the aforementioned container design, which are,
 * as of the time of this writing, `list' and `stack'. So the flavors of those
 * containers would be `elist', `olist', and `estack', `ostack', respectively.)
 *
 * The `e' flavor (`e' stands for `embedded object') containers store objects
 * by making a *copy* (using the copy constructor or operator=() of the object)
 * of the object in the internal node structures. When an object is taken out
 * of the container, a *copy* is made and returned, and the original is
 * destroyed. This flavor is useful for scalar variables and small classes
 * which has insignificant copying overhead.
 *
 * The `o' flavor (`o' stands for `owner pointer') containers store objects
 * by storing a *pointer* to the object to be contained, and returning this
 * pointer when the object is removed from the container. No copies are made.
 * However, this pointer is an "owner" pointer -- meaning that this pointer is
 * considered the `master pointer' of the object -- if the object is still
 * inside the container when the container destructs, the destructor will
 * *deallocate* the object via the pointer. The `ownership' of the object
 * is transferred to the container when the pointer is passed into it; and the
 * ownership of the object is passed back to the caller when the object is
 * removed from the container. While the container owns the object, it is
 * responsible for proper management of the memory associated with the object;
 * hence the object is destructed when the container destructs, unless the
 * object is removed from the container first. All this means that the `o'
 * flavor containers may ONLY store dynamically-allocated objects.
 *
 * Note that the `o' flavor is absolutely NOT equivalent to an `e' flavor
 * with a pointer to the object -- an `e' flavor container may only store
 * `reference pointers', not owner pointers: i.e., the `e' flavor container
 * never assumes ownership of the object the pointer points to, and when the
 * container destructs, any object pointed to by the container is NOT
 * deallocated. (In this respect, the stored pointers behave exactly as
 * scalars).
 */

#ifndef DLIST_H
#define DLIST_H


/* CLASSES */

class _dlistnode {
  friend class _dlistbase;
  friend class _dlistiter;
protected:
  _dlistnode *prev;			// [own]
  _dlistnode *next;			// [own]

  _dlistnode() : prev(0), next(0) {}
public:
  virtual ~_dlistnode();		// destruct derived classes properly
};

class _dlistiter {
  friend class _dlistbase;
protected:
  _dlistnode *ptr;			// [ref]

  _dlistiter(_dlistnode *node) : ptr(node) {}
public:
  // operator int() returns a boolean value indicating whether the
  // iterator is pointing to a valid dlist node or not. When operator++()
  // steps beyond the end of the list, operator int() will return false; so
  // this is useful for stopping loops involving iterators.
  inline operator int() { return (ptr!=0); }
  inline void invalidate() { ptr=0; }	// useful when NULL stands for
					// something (eg. insert at list head)
  inline _dlistiter &operator-- () {
    if (ptr) ptr=ptr->prev;
    return *this;
  }
  inline _dlistiter &operator++ () {
    if (ptr) ptr=ptr->next;
    return *this;
  }
};

class _dlistbase {
protected:
  _dlistnode *head, *tail;		// [own][own]
  int count;
public:
  _dlistbase() : head(0), tail(0), count(0) {}
  void clear();
  ~_dlistbase() { clear(); }

  void operator=(_dlistbase &l);

  // Note: insert() takes owner ptr to n.
  // Note: set succ=NULL to append to list. Note: *n MUST initially have
  // next and prev set to NULL, otherwise this code will malfunction.
  void insert(_dlistnode *n, _dlistnode *succ);

  // Note: remove() returns owner ptr.
  _dlistnode *remove(_dlistnode *n);

  inline void append(_dlistnode *n) { insert(n, 0); }
  inline void prepend(_dlistnode *n) { insert(n, head); }

  inline int empty() {
    return head==0;
  }
  inline int num_elem() {
    return count;
  }
};


/* PRIVATE TEMPLATES */

// forward declarations
template <class type> class edlist;
template <class type> class edlistiter;
template <class type> class edlistnode : public _dlistnode {
  friend class edlist<type>;
  friend class edlistiter<type>;
  type data;

  edlistnode(type d) : data(d) {}
  ~edlistnode() {}
};

template <class type> class odlist;
template <class type> class odlistiter;
template <class type> class odlistnode : public _dlistnode {
  friend class odlist<type>;
  friend class odlistiter<type>;
  type *ptr;				// owner ptr to contained object

  odlistnode(type *p) : ptr(p) {}
  ~odlistnode() { if (ptr) delete ptr; }
};


/* PUBLIC TEMPLATES */

// Template for edlist iterator.
template <class type> class edlistiter : public _dlistiter {
  friend class edlist<type>;
protected:
  edlistiter(edlistnode<type> *node) : _dlistiter(node) {}
public:
  edlistiter() : _dlistiter(0) {}

  inline type &operator*() { return ((edlistnode<type> *)ptr)->data; }
  inline edlistiter<type> &operator++() {
    _dlistiter::operator++();
    return *this;
  }
  inline edlistiter<type> operator++(int) {
    edlistiter<type> old(*this);
    _dlistiter::operator++();
    return old;
  }
  inline edlistiter<type> &operator--() {
    _dlistiter::operator--();
    return *this;
  }
  inline edlistiter<type> operator--(int) {
    edlistiter<type> old(*this);
    _dlistiter::operator--();
    return old;
  }
};

template <class type> class edlist : public _dlistbase {
public:
  edlist() {}
  ~edlist() {}

  inline void append(type data) {
    edlistnode<type> *n = new edlistnode<type>(data);
    _dlistbase::append(n);
  }
  inline void prepend(type data) {
    edlistnode<type> *n = new edlistnode<type>(data);
    _dlistbase::prepend(n);
  }
  inline void insert(type data, edlistiter<type> &succ) {
    edlistnode<type> *n = new edlistnode<type>(data);
    _dlistbase::insert(n, succ.ptr);
  }

  // Note: this will malfunction if n==NULL.
  type remove(edlistiter<type> n) {
    edlistnode<type> *node;
    type data;

    // disconnect node from list
    node = (edlistnode<type> *)_dlistbase::remove(n.ptr);

    // return contained value
    data=node->data;
    delete node;
    return data;
  }
  edlistiter<type> headp() {
    return edlistiter<type>((edlistnode<type>*)head);
  }
  edlistiter<type> tailp() {
    return edlistiter<type>((edlistnode<type>*)tail);
  }
};

// Template for odlist iterator.
template <class type> class odlistiter : public _dlistiter {
  friend class odlist<type>;
protected:
  odlistiter(odlistnode<type> *node) : _dlistiter(node) {}
public:
  odlistiter() : _dlistiter(0) {}

  inline type *operator*() { return ((odlistnode<type> *)ptr)->ptr; }
  inline odlistiter<type> &operator++() {
    _dlistiter::operator++();
    return *this;
  }
  inline odlistiter<type> operator++(int) {
    odlistiter<type> old(*this);
    _dlistiter::operator++();
    return old;
  }
  inline odlistiter<type> &operator--() {
    _dlistiter::operator--();
    return *this;
  }
  inline odlistiter<type> operator--(int) {
    odlistiter<type> old(*this);
    _dlistiter::operator--();
    return old;
  }
};

template <class type> class odlist : public _dlistbase {
public:
  odlist() {}
  ~odlist() {}

  inline void append(type *data) {
    odlistnode<type> *n = new odlistnode<type>(data);
    _dlistbase::append(n);
  }
  inline void prepend(type *data) {
    odlistnode<type> *n = new odlistnode<type>(data);
    _dlistbase::prepend(n);
  }
  inline void insert(type *data, odlistiter<type> &succ) {
    odlistnode<type> *n = new odlistnode<type>(data);
    _dlistbase::insert(n, succ.ptr);
  }

  // Note: this will malfunction if n==NULL.
  type *remove(odlistiter<type> n) {
    odlistnode<type> *node;
    type *data;

    // disconnect node from list
    node = (odlistnode<type> *)_dlistbase::remove(n.ptr);

    // return contained value
    data=node->ptr;
    node->ptr=0;			// else dtor will trash returned data!
    delete node;
    return data;
  }
  odlistiter<type> headp() {
    return odlistiter<type>((odlistnode<type>*)head);
  }
  odlistiter<type> tailp() {
    return odlistiter<type>((odlistnode<type>*)tail);
  }
};


#endif // DLIST_H
