#include <iostream>
#include <string>
#include <new>     //so you can use the new command
#include <cassert> //for debugging

using namespace std;

#include "DAStack.h"
#include "DAStackExceptions.h"


    /* default constructor */
    DAStack::DAStack()
    : capacity(DEFAULT),top(NULLVAL)
    {
      const string MESSAGE = "DEFAULT CONSTRUCTOR";
      size = top+1;
      elements = new(nothrow) Element[capacity];
      verifyAllocation(MESSAGE, elements); //exits if allocation was unsuccessful
      cout << MESSAGE << endl;
    }


    /* explicit value constructor */
    DAStack::DAStack(int initialCapacity)
    : capacity(initialCapacity),top(NULLVAL)
    {
      const string MESSAGE = "EXPLICIT VALUE CONSTRUCTOR";
      size = top+1;
      elements = new(nothrow) Element[capacity];
      verifyAllocation(MESSAGE, elements); //exits if allocation was unsuccessful 
      cout << MESSAGE << endl;
    }

    /* copy constructor */
    DAStack::DAStack(const DAStack & original)
    : capacity(original.capacity),top(original.top)    
    {
      const string MESSAGE = "COPY CONSTRUCTOR";
      size = top + 1;
      elements = new(nothrow) Element[capacity];
      verifyAllocation(MESSAGE, elements); //exits if allocation was unsuccessful 
      for (int i=0; i<(top+1); i++)
      {
        elements[i] = original.elements[i];
      }
      cout << MESSAGE << endl;
    }

	
    /* destructor */
    DAStack::~DAStack( )
    {
      const string MESSAGE = "DESTRUCTOR";
      delete [] elements;
      cout << MESSAGE << endl;
    }

    /* predicate */
    bool DAStack::isEmpty()
    {
      const string MESSAGE = "IS_EMPTY";
      cout << MESSAGE << endl;
      return (top == NULLVAL);
    }
  
    /* mutator */
    void DAStack::push(Element element)
    {
      const string MESSAGE = "PUSH";
      const int DOUBLE = 2;      
      if (size == capacity)
      {
        /* grow the dynamic array */
        capacity*=DOUBLE; //double the size
        modifyArraySize(MESSAGE);
      }
      top++;
      elements[top] = element; 
      size++;
      cout << MESSAGE << endl;
    }

    /* mutator */
    void DAStack::pop() throw (Empty)
    {
      const string MESSAGE = "POP";
      const int HALF = 2;
      const int QUARTERS = 4; //To measure filled vs. capacity
      if (top > NULLVAL)
      {
        top--;
        size--;
        /* Now shrink array if less than one-quarter full */
        if (capacity > size * QUARTERS)
        {
          capacity/=HALF; //shrink by half
          modifyArraySize(MESSAGE);
        }
      }
      else
      {
        throw Empty(MESSAGE);
      }
      cout << MESSAGE << endl;
    }

    /* accessor */
    int DAStack::getSize() const
    {
      const string MESSAGE = "GET_SIZE";
      cout << MESSAGE << endl;
      return size;
    }


    /* accessor */
    Element DAStack::getTop() const throw (Empty)
    {
      const string MESSAGE = "GET_TOP";
      if (top > NULLVAL)
      {
        cout << MESSAGE << endl;
      }
      else
      {
	throw Empty(MESSAGE);
      }
      return elements[top];
    }


    /* output */
    void DAStack::display(ostream & out) const
    {
      const string MESSAGE = "DISPLAY";
      cout << "This DAStack object holds " << size << " Element(s), and can hold "<< capacity << " Elements." << endl; 
      if (top > NULLVAL)
      {
        cout << "The Elements in this DAStack are: " << endl;
        for(int i=top; i > NULLVAL; i--)
          cout << "\t" << elements[i];
        cout << endl;
      }
      cout << MESSAGE << endl;
    }

    void DAStack::read(istream & in)
    {
      const string MESSAGE = "READ";
      Element element;
      char c;
      while (in>>element)
      {
        push(element);
      }
      in >> c;
      cout << MESSAGE << endl;            
    }
    
    /* overloaded assignment operator */
    DAStack & DAStack::operator=(const DAStack & original)
    {
      const string MESSAGE = "OVERLOADED=";
      if (this != &original)
      {
        capacity = original.capacity;
        top = original.top;
        size = original.size;
        delete [] elements;
        elements = new(nothrow) Element[capacity];
        verifyAllocation(MESSAGE, elements);
        for (int i=0; i<size; i++)
        {
          elements[i] = original.elements[i];
        }
      }
      cout << MESSAGE << endl;
      return *this;
    }

    /* modify array size */
    void DAStack::modifyArraySize(const string message)
    {
      const string MESSAGE = "MODIFY ARRAY SIZE";
     ElementsInStack tempPtr = new(nothrow) Element[capacity];
      verifyAllocation(MESSAGE + " from " + message, tempPtr); //exits if allocation was unsuccessful
      for (int i=0; i<size; i++)
        tempPtr[i] = elements[i];
      delete [] elements;
      elements = tempPtr;
      cout << MESSAGE << endl;
    }

    /* verify dynamic allocation */
    bool DAStack::verifyAllocation(const string messageSource,ElementsInStack newPtr)
    {
      const string MESSAGE = "VERIFY ALLOCATION";
      if (newPtr == 0)
      {
        cerr << "** Out of memory: " << messageSource << " **" << endl;
        exit(1);
      }
      else
      {
        cout << MESSAGE << endl;
        return true;
      }
    }

    /* overloaded ouput operator */
    ostream & operator<<(ostream & out, const DAStack & stack)
    {
      const string MESSAGE = "OVERLOADED<<";
      stack.display(out);
      cout << MESSAGE << endl;
      return out;
    }

    istream & operator>>(istream & in, DAStack & stack)
    {
      const string MESSAGE = "OVERLOADED>>";
      stack.read(in);
      cout << MESSAGE << endl;      
      return in;
    }



