#ifndef DLL_H_
#define DLL_H_ 1

// Oldham, Jeffrey D.
// 2000Mar13
// CS1321

// Modified by:
// Massingill, Berna L.
// 2001 March

// Doubly-Linked List Implementation

// This class implements a doubly-linked list.  Each link is allocatd
// from dynamic memory.  The list is represented as a circular-linked
// list with a sentinel link.

#include <iostream>
#include <assert.h>		// has assert()

class dll {
public:
  typedef char item_type;	// for now -- "templatize" later

private:
  class link {
  public:
    item_type itm;
    link * prev;                // pointers to other links
    link * next;
  };

public:

  // Constructor.
  dll(void) { create(); return; }

  // Copy constructor.
  dll(const dll & d) { copy(d); return; }

  // Assignment operator.
  dll& operator=(const dll & d) {
    if (this != &d) {
      destroy();
      copy(d);
    }
    return *this;
  }

  // Destructor.
  ~dll(void) {
    // Delete all the links.
    destroy();
  }

  // Insert the item before the link.
  // Post:  a link with value "i" has been added before link *pos.
  //        returns pointer to newly-added link.
  link * insert(link * const pos, const item_type & i) {
    link * n = new link;
    n->itm = i;

    // Change new link's pointers.
    n->prev = pos->prev;
    n->next = pos;

    // Change the surrounding links' pointers.
    n->next->prev = n;
    n->prev->next = n;
    return n;
  }

  // Delete the specified link.
  // Pre:  *pos is not the sentinel link.
  // Post:  link *pos has been removed from the list.
  //        returns pointer to link following *pos.
  link * erase(link * const pos) {
    assert(pos != sentinel);
    link * n = pos;		// or we could have passed "pos" by value
    link * answer = n->next;

    // Change the surrounding links' pointers.
    n->prev->next = n->next;
    n->next->prev = n->prev;

    // Change new link's pointers (not actually necessary).
    n->prev = 0;
    n->next = 0;

    delete n;
    return answer;
  }

  // Get start of list (analogous to STL classes' begin()).
  // Post:  returns pointer to first link in list (or to sentinel,
  //          if list is empty).
  link * begin(void) const { return sentinel->next; } 

  // Get past-end of list (analogous to STL classes' end()).
  // Post:  returns pointer to sentinel link.
  link * end(void) const { return sentinel; }

  // Get predecessor link.
  // Post:  returns pointer to link before *pos.
  link * pred(link * const pos) const { return pos->prev; }

  // Get successor link.
  // Post:  returns pointer to link after *pos.  
  //        if *pos is the last link in the last, this function
  //          should return the same thing as the end() function;
  //          callers can determine "end of list" by comparing the
  //          value returned by succ() to the value returned by 
  //          end().
  link * succ(link * const pos) const { return pos-> next; }

  // Output operator.
  friend ostream & operator << (ostream & o, const dll & d) {
    for (link * p = d.begin();
         p != d.end();
	 p = d.succ(p))
      o << p->itm << ' ';
    return o;
  }

private:
  link * sentinel;              // sentinel (dummy) link, always present

  // Create an empty list.
  // Pre:  sentinel does not point to anything; no "link" objects are
  //         allocated for this list.
  // Post:  sentinel points to the "sentinel" node, which is the only
  //          link in the list.
  void create(void) {
    sentinel = new link;
    // sentinel->itm need not be set
    sentinel->prev = sentinel;
    sentinel->next = sentinel;
    return;
  }

  // Destroy this list.
  // Pre:  sentinel points to sentinel link for list.
  // Post:  all allocated "link" objects have been freed.
  void destroy(void) {
    while (sentinel->next != sentinel)
      erase(sentinel->next);
    delete sentinel;
  }

  // Copy the given list into this one.
  // Pre:  sentinel does not point to anything; no "link" objects are
  //         allocated for this list.
  // Post:  list is a copy of list "d".
  void copy(const dll & d) {
    create();
    for (link * pos = d.sentinel->next; pos != d.sentinel; pos = pos->next)
      insert(sentinel, pos->itm);
    return;
  }
};

#endif // DLL_H_