// ---------------------------------------------------------------------------
// SimpleUnboundedQueue.h
//
// A generic unbounded queue class.  (There is already a queue class in the 
// Standard C++ Library; this class is just an illustration of how such a
// class is written from scratch!)  Copying and assignment are prohibited 
// because this is just a simple example.  A real class would have copy
// constructors and an assignment operator defined.
//
//   Queue()       Constructor: makes an empty queue.
//   ~Queue()      Destructor.
//   q.getSize()   Returns the number of elements in the queue.
//   q.isEmpty()   Returns whether the queue is empty.
//   q.enqueue(x)  Adds x to the back of q.   Will throw a bad_alloc if there
//                 is no memory available to add the element (but note that
//                 many compilers do not implement the C++ Standard correctly
//                 and with one of these the behavior is undefined is there
//                 is no memory).
//   q.dequeue()   If not empty, removes the front element and returns it,
//                 else throws a standard underflow_error.
//   q.peek()      If not empty, returns the front element, else throws
//                 a standard underflow_error.
// ---------------------------------------------------------------------------

#ifndef _SIMPLE_UNBOUNDED_QUEUE_H
#define _SIMPLE_UNBOUNDED_QUEUE_H

#include <stdexcept>
using namespace std;

// A queue object is represented as a singly-linked list of nodes, from the
// head to the tail.  The queue itself maintains pointer to the head and the
// tail nodes.

template<class T>
class Queue {

// A private local class for the nodes that are linked together.
private:
  class Node {
  public:
    T data;
    Node* next;
    Node(T data, Node* next): data(data), next(next) {}
  };

// The queue itself maintains pointers to the head node and tail node.
private:
  Node* head;
  Node* tail;

// A private helper function to delete all the nodes in the queue.
// It is inefficient but it looks cool.  It deletes all nodes
// from its argument onward.
private:
  static void clear(Node* n) {if (n != 0) {clear(n->next); delete n;}}

// Constructors and Destructors
public:

  // Constructor ensures that the queue is empty upon creation.
  Queue(): head(0), tail(0) {}

  // Destructor uses the helper to delete all the nodes.
  ~Queue() {clear(head);}

// Behavior
public:

  // The size of the queue is determined by mindless counting of nodes.
  int getSize() {
    int count = 0;
    for (Node* n = head; n != 0; n = n->next) count++;
    return count;
  }

  // A queue is empty when its head and tail pointers are both null.  Since
  // the implementation maintains this consistency requirement, it suffices
  // to simply check one or the other to determine emptiness.
  bool isEmpty() {return head == 0;}

  // To add an element we make a new node, attach it after the current tail
  // node, and update the queue's tail filed to point to this new node.  One
  // special case is when the queue is initially empty: in this case the
  // new node becomes both the head and the tail.
  void enqueue(T x) {
    if (tail == 0)
      head = tail = new Node(x, 0);
    else
      tail = tail->next = new Node(x, 0);
  }

  // Removing from a queue is taking the head element out.  First of all if
  // there are no elements we have to throw an error because there is nothing
  // to remove.  If there is, we save the head element to return later as
  // well as a pointer to the head node to delete later.  We "detach" the node
  // by moving the queue's head pointer past it.  Note the special case here!
  // If this was the last node, we have to update the queue's tail node field!
  // After all this, we can delete the node and return the value we saved up.
  T dequeue() {
    if (isEmpty()) throw underflow_error("cannot remove from empty queue");
    Node* nodeToDelete = head;
    T valueToReturn = head->data;
    head = head->next;
    if (head == 0) tail = 0;
    delete nodeToDelete;
    return valueToReturn;
  }

  // Peek just returns the head element, if there is one.
  T peek() {
    if (head == 0) throw underflow_error("Cannot peek into empty queue");
    return head->data;
  }

// Prohibit copying and assignment
private:
  Queue(Queue&);
  Queue& operator=(Queue&);
};

#endif
// ---------------------------------------------------------------------------
// testunboundedqueue.cpp                                                                 
//                                                                           
// A test script for the SimpleUnboundedQueue class.
// ---------------------------------------------------------------------------

#include <iostream>
#include <cassert>
#include <stdexcept>
using namespace std;

#include "SimpleUnboundedQueue.h"

int main(int argc, char** argv) {

  // Test an integer queue of testSize 200 for empty, getSize, and that
  // enqueing then dequeueing produces predicatble (FIFO) results.
  
  const int testSize = 8;
  Queue<int> q;

  assert(q.isEmpty() && q.getSize() == 0);
  q.enqueue(0);
  assert(!q.isEmpty() && q.getSize() == 1);
  for (int i = 1; i < testSize; i++) {
    q.enqueue(i);
  }
  assert(!q.isEmpty() && q.getSize() == testSize);
  for (int i = 0; i < testSize; i++) {
    assert(q.peek() == i);
    assert(q.dequeue() == i);
  }

  // Test underflow_errors are properly thrown on dequeues and peeks
  // when empty.  Also test that if an underflow is thrown, the queue
  // is not disturbed, i.e., it remains empty.
  
  assert(q.isEmpty());
  bool underflowHappened = false;
  try {
    q.dequeue();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  assert(q.isEmpty());
  underflowHappened = false;
  try {
    q.peek();
  } 
  catch (underflow_error e) {
    underflowHappened = true;
  }
  assert(underflowHappened);
  
  cout << "All tests have passed\n";
  return 0;
}