/*
 * Simple singly-linked list class & templates
 * Header file
 * $Id: list.h,v 1.10 2003/04/14 14:35:03 hsteoh Exp hsteoh $
 * ----------------------------------------------------------------------------
 * USAGE
 *
 * Ordinary users of the class should only use the templates 'elist' and
 * 'olist'. The other classes are implementation-specific and should not be
 * used directly unless you're implementing another 'flavour' of list besides
 * the 'e' and 'o' flavours.
 *
 * The difference between these two flavours of lists is the way the contained
 * objects are stored.
 *
 * In the 'e' flavour of the list, implemented as the template 'elist', objects
 * are passed directly as parameters to methods like append(), prepend(),
 * insert(), etc.. These functions then use the copy constructor of the object
 * to make a copy of the object, and stores this copy in the list. Likewise,
 * when an object is removed from the list, its copy constructor is used to
 * make a copy which is then returned to the caller.
 *
 * In the 'o' flavour of the list, implemented as the template 'olist', the
 * *pointer* to the object to be stored in the list is passed. No copy
 * constructor is used; the list simply stores a pointer to the object. When
 * the object is removed from the list, a pointer to it is returned, again
 * without any invocation of copy constructors (or some such method). Note,
 * however, that these object pointers are 'owner' pointers, meaning that when
 * the pointer is passed to the list, the object from then on is 'owned' by the
 * list -- the list is responsible for destroying the object if the list
 * destructor is called; and when the pointer is returned from the list, the
 * responsibility is passed back to the caller.
 *
 * The 'e' flavour is primarily meant for storing atomic types or small objects
 * whose copy constructors are small and efficient. The 'o' flavour is meant
 * for storing potentially large objects that you do not want to keep making
 * copies of just for the sake of moving it in and out of lists.
 *
 * Note that the 'o' flavour is NOT a syntactic shortcut to instantiating the
 * 'e' flavour with a pointer to the object type. The instance elist<obj*>
 * will store only pointers to the object, but will not maintain the object
 * pointed to. An olist<obj> also stores obj* pointers, but when an olist<obj>
 * is destructed, it calls the destructor of every object that is still
 * contained in it.
 */

#ifndef LIST_H
#define LIST_H

#include <stack.h>			// must be the prog/lib/ version.


/* PRIVATE CLASSES
 * These are not meant to be used directly; normal users should only use
 * the public templates provided.
 */

// This provides class _listbase with access to protected members in
// _stacknode.
class _listnode : public _stacknode {
  friend class _listbase;
  friend class _listiter;
};

// This class provides class _listbase with access to protected members in
// class _stackiter.
class _listiter : public _stackiter {
  friend class _listbase;
protected:
  _listiter(_listnode *node) : _stackiter(node) {}
};

// Singly-linked list base class. Built by extending stack base.
class _listbase : public _stackbase {
protected:
  _listnode *bottom;			// tail of list
  int count;				// count number of elements in list
public:
  _listbase() { bottom=0; count=0; }
  void clear() { _stackbase::clear(); bottom=0; count=0; }
  ~_listbase() { count=0; }		// (base class automatically calls
					//  _stackbase::clear().)

  /* Operations */

  inline void prepend(_listnode *n) {
    if (!bottom) bottom=n;		// special case when *bottom needs to
					// be updated.
    push(n);				// prepend()==base class::push().
    count++;
  }
  inline void append(_listnode *n) {
    if (bottom) {
      bottom->next = n;
    } else {				// special case: list is empty
      push(n);				// so prepend=append=push.
    }
    bottom = n;
    n->next = 0;
    count++;
  }
  // Inserts a node *after* the node pointed to by &where. If iterator
  // is NULL, inserts at head of list.
  // Be ABSOLUTELY SURE that the iterator actually points to something within
  // the list! otherwise you'll completely screw up your lists.
  inline void insert(_listnode *n, _listiter &where) {
    if (!where.ptr) {
      prepend(n);			// special case: insert at head of list
    } else {
      n->next = ((_listnode*)where.ptr)->next;
      ((_listnode*)where.ptr)->next = n; // attach new node after current node
      if (((_listnode*)where.ptr)==bottom)
        bottom=n;			// tail of list has moved.
      count++;
    }
  }
  // Removes the SUCCESSOR of &pred. If &pred is NULL, removes the head
  // element of the list.
  // Returns the removed node. The caller is responsible for deallocating
  // it, etc..
  _listnode *remove(_listiter &pred);

  /* Queries */
  inline int num_elem() { return count; }
};


/* PRIVATE TEMPLATES */

template <class type> class elist;	// (forward declaration)
template <class type> class elistiter;	// (forward declaration)
template <class type>
class elistnode : public _listnode {
  friend class elist<type>;
  friend class elistiter<type>;
  type data;

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

template <class type>
class elistiter : public _listiter {
  friend class elist<type>;
protected:
  elistiter(elistnode<type> *node) : _listiter(node) {}
public:
  elistiter() : _listiter(0) {}

  type &operator*() { return ((elistnode<type> *)ptr)->data; }
  elistiter<type> &operator++() {
    _listiter::operator++();
    return *this;
  }
  elistiter<type> operator++(int) {
    elistiter old(*this);
    _listiter::operator++();
    return old;
  }
};

template <class type> class olist;	// (forward declaration)
template <class type> class olistiter;	// (forward declaration)
template <class type>
class olistnode : public _listnode {
  friend class olist<type>;
  friend class olistiter<type>;
  type *data;

  olistnode(type *d) : data(d) {}
  ~olistnode() { if (data) delete data; }
};

template <class type>
class olistiter : public _listiter {
  friend class olist<type>;
protected:
  olistiter(olistnode<type> *p) : _listiter(p) {}
public:
  olistiter() : _listiter(0) {}

  type *operator*() { return ((olistnode<type> *)ptr)->data; }
  olistiter<type> &operator++() {
    _listiter::operator++();
    return *this;
  }
  olistiter<type> operator++(int) {
    olistiter old(*this);
    _listiter::operator++();
    return old;
  }
};


/* PUBLIC TEMPLATES */

template <class type>
class elist : public _listbase {
public:
  void append(type data) {
    elistnode<type> *n = new elistnode<type>(data);
    _listbase::append(n);
  }
  void prepend(type data) {
    elistnode<type> *n = new elistnode<type>(data);
    _listbase::prepend(n);
  }
  void insert(type data, elistiter<type> &where) {
    elistnode<type> *n = new elistnode<type>(data);
    _listbase::insert(n, where);
  }

  // If the node to be removed doesn't exist, the default value for
  // the given type is returned (for atomic types, this may be a junk value).
  // The safest bet is still to check the iterator yourself before calling
  // this function.
  type remove(elistiter<type> &pred) {
    elistnode<type> *p;
    type data;

    p = (elistnode<type> *)_listbase::remove(pred);
    if (p) {
      data=p->data;
      delete p;				// deallocate node
    }
    return data;
  }
  elistiter<type> headp() {
    return elistiter<type>((elistnode<type> *)_top);
  }
  elistiter<type> tailp() {
    return elistiter<type>((elistnode<type> *)bottom);
  }
};

template <class type>
class olist : public _listbase {
public:
  void append(type *ptr) {
    olistnode<type> *n = new olistnode<type>(ptr);
    _listbase::append(n);
  }
  void prepend(type *ptr) {
    olistnode<type> *n = new olistnode<type>(ptr);
    _listbase::prepend(n);
  }
  void insert(type *ptr, olistiter<type> &where) {
    olistnode<type> *n = new olistnode<type>(ptr);
    _listbase::insert(n, where);
  }
  // Returns NULL if the node to be removed doesn't exist.
  // An owner pointer to the removed object is returned: ie. the caller is
  // responsible for destructing this object unless the pointer is NULL.
  type *remove(olistiter<type> &pred) {
    olistnode<type> *p;
    type *d=0;

    p = (olistnode<type> *)_listbase::remove(pred);
    if (p) {
      d = p->data;
      p->data = 0;			// so that olistnode dtor doesn't
					// delete data object.
      delete p;				// discard list node
    }
    return d;				// return retrieved data
  }
  olistiter<type> headp() {
    return olistiter<type>((olistnode<type> *)_top);
  }
  olistiter<type> tailp() {
    return olistiter<type>((olistnode<type> *)bottom);
  }
};


#endif // LIST_H
