/***** RStackT1.h *****/
/* RStackT1.h provides a Stack template.
 *
 * Receives:  Type parameter StackElement
 * Basic operations:
 *   Constructor:  Constructs an empty stack
 *   empty:   Checks if a stack is empty
 *   push:    Modifies a stack by adding a value at the top
 *   top:     Accesses the top stack value; leaves stack unchanged
 *   pop:     Modifies a stack by removing the value at the top
 *   display: Displays all the stack elements
 * Class Invariant:
 *   1. The stack elements (if any) are stored in positions
 *      0, 1, . . ., myTop_ of myArrayPtr.
 *   2. -1 <= myTop_ <= STACK_CAPACITY
 ------------------------------------------------------------------*/

#ifndef RTSTACK1
#define RTSTACK1

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

template <typename StackElement>
class Stack
{
/***** Function Members *****/
public:
/* --- Class constructor ---
 * Precondition:  A stack has been defined.
 * Receive:       Integer numElements > 0; (default = 128)
 * Postcondition: The stack has been constructed as a stack
 *                  with capacity numElements.
 **************************************************************/
Stack(int numElements = 128);

/* --- Class Copy Constructor ---
 * Precondition:  A copy of a stack is needed
 * Receive:       The stack to be copied (as a const
 *                  reference parameter)
 * Postcondition: A copy of original has been constructed.
 ************************************************************/
Stack(const Stack<StackElement> & original);

/* --- Class destructor ---
 * Precondition:  The lifetime of the Stack containing this
 *                  function should end.
 * Postcondition: The run-time array in the Stack containing
 *                  this function has been deallocated.
 ************************************************************/
 ~Stack();

/* --- Is the Stack empty? ---
 *
 * Return: True iff the Stack containing this function is empty
 **************************************************************/
bool empty() const;

/* --- Add a value to the stack ---
 *
 * Receive: The Stack containing this function (implicitly)
 *          A value to be added to a Stack
 * Return:  The Stack (implicitly), with value added at its
 *          top, provided there's space
 * Output:  Stack-full message if no space for value.
 **************************************************************/
void push(const StackElement & value);

/* --- Return value at top of the stack ---
 *
 * Receive: The Stack containing this function (implicitly)
 * Return:  The value at the top of the Stack (if nonempty)
 **************************************************************/
StackElement top();

/* --- Remove value at top of the stack ---
 *
 * Receive: The Stack containing this function (implicitly)
 * Return:  The Stack containing this function with its top
 *          value (if any) removed
 * Output:  "Stack-empty" message if stack is empty.
 **************************************************************/
void pop();

/* --- Display values stored in the stack ---
 *
 * Receive: The Stack containing this function (implicitly)
 *          The ostream out
 * Output:  The Stack's contents, from top down, to out
 *************************************************************/
void display(ostream & out) const;

/* --- Assignment Operator ---
 *  Receive: Stack stRight (the right side of the assignment operator)
 *             object containing this member function
 *  Return (implicit parameter):  The Stack containing this
 *             function which will be a copy of stRight
 *   Return (function): A reference to the Stack containing
 *             this function
 ************************************************************/
Stack<StackElement> & operator=(const Stack & original);


/***** Data Members *****/
private:
  StackElement * myArrayPtr;  // run-time allocated array
  int myCapacity_,            // capacity of stack
      myTop_;                 // top of stack
};  // end of class declaration


//*** Definition of class constructor
template <typename StackElement>
Stack<StackElement>::Stack(int numElements)
{
  assert (numElements > 0);   // check precondition
  myCapacity_ = numElements;  // set stack capacity
                              // allocate array of this capacity
  myArrayPtr = new StackElement[myCapacity_];

  if (myArrayPtr == 0)        // check if memory available
  {
    cout << "*** Inadequate memory to allocate stack ***\n";
    exit(-1);
  }                                        // or assert(myArrayPtr != 0);

  myTop_ = -1;
}

//*** Definition of copy constructor
template <typename StackElement>
Stack<StackElement>::Stack(const Stack<StackElement> & original)
{
  cout << "COPY CONSTRUCTOR\n";
  myCapacity_ = original.myCapacity_;         // copy myCapacity_ member
  myArrayPtr = new StackElement[myCapacity_];   // allocate array in copy

  if (myArrayPtr == 0)                          // check if memory available
  {
    cout << "*** Inadequate memory to allocate stack ***\n";
    exit(-1);
  }

  for (int pos = 0; pos < myCapacity_; pos++) // copy array member
    myArrayPtr[pos] = original.myArrayPtr[pos];
  myTop_ = original.myTop_ ;                  // copy myTop_ member
}

//*** Definition of class destructor
template <typename StackElement>
Stack<StackElement>::~Stack()
{
  cout << "DESTRUCTOR\n";
  delete[] myArrayPtr;
}

//*** Definition of empty()
template<typename StackElement>
inline bool Stack<StackElement>::empty() const
{
  return (myTop_ == -1);
}

//*** Definition of push()
template <typename StackElement>
void Stack<StackElement>::push(const StackElement & value)
{
  if (myTop_ < myCapacity_ - 1)
  {
    ++myTop_;
    myArrayPtr[myTop_] = value;
  }                  // or simply, myArrayPtr[++myTop_] = value;
  else
    cout << "*** Stack is full -- can't add new value ***\n";
}

//*** Definition of top()
template <typename StackElement>
StackElement Stack<StackElement>::top()
{
  if (myTop_ >= 0)
    return (myArrayPtr[myTop_]);
  cout << "*** Stack is empty -- returning 'garbage value' ***\n";
  return myArrayPtr[myCapacity_ -1];
}

//*** Definition of pop ()
template <typename StackElement>
void Stack<StackElement>::pop()
{
  if (myTop_ >= 0)
    myTop_--;
  else
    cout << "*** Stack is empty -- can't remove a value ***\n";
}

//--- Definition of display()
template <typename StackElement>
void Stack<StackElement>::display(ostream & out) const
{
  for (int i = myTop_; i >= 0; i--)
    out << myArrayPtr[i] << endl;
}

//*** Definition of operator=() 
template <typename StackElement>
Stack<StackElement> & 
   Stack<StackElement>::operator=(const Stack<StackElement> & original)
{
  if (this != &original)                        // check that not st = st
  {
    delete[] myArrayPtr;                        // destroy previous array

    myArrayPtr = new StackElement[myCapacity_];   // allocate array in copy
    if (myArrayPtr == 0)                          // check if memory available
    {
      cout << "*** Inadequate memory to allocate stack ***\n";
      exit(-1);
    }

    myCapacity_ = original.myCapacity_;         // copy myCapacity_ member

    for (int pos = 0; pos < myCapacity_; pos++) // copy array member
      myArrayPtr[pos] = original.myArrayPtr[pos];
    myTop_ = original.myTop_ ;                  // copy myTop_ member
  }

  return *this;                                // return this object
}


#endif
