/*
 * Default Constructor:  creates an empty list
 */
template <typename Object>
NodeList<Object>::NodeList() : sz(0) {
  header   = new Node;				// create sentinels
  trailer  = new Node;
  header->next   = trailer;			// head points to trailer
  trailer->prev  = header;			// trailer points to head
}




/*
 * returns size of list
 */
template <typename Object>
int NodeList<Object>::size() const {
  return sz;
}


/*
 * is the list empty?
 */
template <typename Object>
bool NodeList<Object>::isEmpty() const {
  return (sz==0);
}


/*
 * return position of first element in list
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::first() const throw(EmptyContainerException) {
  if (isEmpty()) throw EmptyContainerException("List is empty");
  return Position(this,header->next);
}


/*
 * return position of last element in list
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::last() const throw(EmptyContainerException) {
  if (isEmpty()) throw EmptyContainerException("List is empty");
  return Position(this,trailer->prev);
}


/*
 * is this the first position?
 */
template <typename Object>
bool NodeList<Object>::isFirst(const Position& p) const throw(InvalidPositionException) {
  NodePtr v = p.validate(this);
  return v->prev == header;
}


/*
 * is this the last position?
 */
template <typename Object>
bool NodeList<Object>::isLast(const Position& p) const throw(InvalidPositionException) {
  NodePtr v = p.validate(this);
  return v->next == trailer;
}


/*
 * return position before the given position
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::before(const Position& p) const
  throw(BoundaryViolationException, InvalidPositionException) {
    NodePtr v = p.validate(this);
    NodePtr prev = v->prev;
    if (prev == header)
      throw BoundaryViolationException("Cannot traverse beyond beginning of list");
    return Position(this,prev);
}


/*
 * return position after the given position
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::after(const Position& p) const
  throw(BoundaryViolationException, InvalidPositionException) {
    NodePtr v = p.validate(this);
    NodePtr next = v->next;
    if (next == trailer)
      throw BoundaryViolationException("Cannot traverse beyond end of list");
    return Position(this,next);
}



/*
 * replace the element at the given position
 */
template <typename Object>
void NodeList<Object>::replaceElement(const NodeList<Object>::Position& p, const Object& element)
  throw(InvalidPositionException) {
    NodePtr v  = p.validate(this);
    v->element = element;
}


/*
 * swap the elements stored at the given positions
 */
template <typename Object>
void NodeList<Object>::swap(const NodeList<Object>::Position& p,
			    const NodeList<Object>::Position& q)
  throw(InvalidPositionException) {
    NodePtr v  = p.validate(this);
    NodePtr w  = q.nodePtr(this);
    Object e = v->element;
    v->element = w->element;
    w->element = e;
}


/*
 * insert new element e into list as the first element
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::insertFirst(const Object& element) {
    sz++;
    NodePtr newNode  = new Node(element, header, header->next);
    newNode->next->prev = newNode;				// link node into list
    newNode->prev->next	= newNode;
    return Position(this,newNode);
}


/*
 * insert new element e into list as the last element
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::insertLast(const Object& element) {
    sz++;
    NodePtr newNode  = new Node(element, trailer->prev, trailer);
    newNode->next->prev = newNode;				// link node into list
    newNode->prev->next	= newNode;
    return Position(this,newNode);
}


/*
 * insert new element before the given position
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::insertBefore(const Position& p, const Object& element)
  throw(InvalidPositionException) {
    NodePtr v = p.validate(this);
    sz++;
    NodePtr newNode  = new Node(element, v->prev, v);
    newNode->prev->next  = newNode;				// link node into list
    newNode->next->prev  = newNode;
    return Position(this,newNode);
}


/*
 * insert new element after the given position
 */
template <typename Object>
typename NodeList<Object>::Position NodeList<Object>::insertAfter(const Position& p, const Object& element)
  throw(InvalidPositionException) {
    NodePtr v = p.validate(this);
    sz++;
    NodePtr newNode  = new Node(element, v, v->next);
    newNode->next->prev = newNode;				// link node into list
    newNode->prev->next	= newNode;
    return Position(this,newNode);
}


/*
 * remove the element at the given position
 */
template <typename Object>
void NodeList<Object>::remove(const Position& p) throw(InvalidPositionException) {
  sz--;
  NodePtr v = p.validate(this);
  v->prev->next    = v->next;				// unlink from the list
  v->next->prev    = v->prev;
  v->next = v->prev = NULL;
  delete v;
}

/*
 * Copy Constructor
 */
template <typename Object>
NodeList<Object>::NodeList(const NodeList<Object>& orig) { cloneList(orig); }

/*
 * Overloaded Assignment Operator
 */
template <typename Object>
NodeList<Object>& NodeList<Object>::operator=(const NodeList<Object>& orig) {
  if (this != &orig) {
    clearList();
    cloneList(orig);
  }
  return *this;
}

/*
 * Destructor
 */
template <typename Object>
NodeList<Object>::~NodeList() {
  clearList();
}


/*
 * clears existing list (including header/trailer)
 */
template <typename Object>
void NodeList<Object>::clearList() {
  while (!isEmpty())
    remove(first());

  delete header;
  delete trailer;
}


/*
 * clones an existing list
 */
template <typename Object>
void NodeList<Object>::cloneList(const NodeList<Object>& orig) {
  sz = 0;             // sz will later be incremented via insertLast
  header = new Node;
  trailer = new Node;
  header->next = trailer;
  trailer->prev = header;
  for (NodePtr n = orig.header->next; n != orig.trailer; n = n->next)
    insertLast(n->element);
}


/*******************************************************
 * Position methods
 *******************************************************/

/**
 * utility to convert Position to node pointer
 */
template <typename Object>
typename NodeList<Object>::NodePtr
     NodeList<Object>::Position::validate(const NodeList<Object>* list) const
  throw(InvalidPositionException) {
  if (node == NULL)
    throw InvalidPositionException("Cannot use a NULL position");
  if (container != list)
    throw InvalidPositionException("Position does not below to this list.");
  if (node->prev==NULL)
    throw InvalidPositionException("Position appears to involve an item which has since been removed from the list.");
  else return node;
}
