#include <iostream>
#include <string>

using namespace std;

class Node
{
private:
    Node* nextNode;
    string data;

public:
    //constructors
    Node();
    Node(string val);
    Node(string val, Node* next);

    //get and set functions
    void setNextNode(Node* next);
    void setData(string n);
    Node* getNextNode();
    string getData();
};


Node::Node()  //default constructor
{
    nextNode = NULL;
    data = -1;
}

Node::Node(string val) //alternate constructor
{
	data = val;
	nextNode = NULL;
}

Node::Node(string val, Node* next) //alternate constructor
{
	data = val;
	nextNode = next;
}

void Node::setNextNode(Node* next) //sets the next node in the list
{
    nextNode = next;
}

void Node::setData(string n) //stores a value in the Node's data element
{
    data = n;
}

Node* Node::getNextNode() //returns the next node in the list
{
    return nextNode;
}

string Node::getData()  //returns the Node's data element
{
    return data;
}
//END NODE CLASS


//BEGIN LIST CLASS
class List
{
private:
    Node* first; //pointer to the first Node in the list
    Node* last; //pointer to the last Node in the list
    int size;  //the number of Nodes in the list

public:
    List();  //constructor
    ~List(); //destructor

    //utility functions
    void append(Node* nodeToAppend);
    void insertAtIndex(int index, Node* nodeToInsert);
    void deleteNodeAtIndex(int index);
    Node* getNodeAtIndex(int index);
    int getSize();
};

List::List()  //constructor for the List class
{
    first = NULL;
    last = NULL;
    size = 0;
}

void List::append(Node* nodeToAppend)
{
    if (last != NULL)
    {
        //the list is not empty, so just add to the end of it
        (*last).setNextNode(nodeToAppend);
        last = nodeToAppend;
    }
    else
    {
        //the list is currently empty, so we need to create the first node
        first = nodeToAppend;
        last = nodeToAppend;
    }
    //increment our size counter
    size++;
}

void List::insertAtIndex(int index, Node* nodeToInsert)
{
    if ((index < size) && (index >= 0))  //make sure we have a valid index
    {
        //increment our size counter
        size++;

        //we only want to attempt an insertion if the specified location
        //is within the bounds of the list
        if (index == 0)
        {
            //this means we are inserting to the very front of the list
            Node* temp = first;
            first = nodeToInsert;
            (*nodeToInsert).setNextNode(temp);
        }
        else
        {
            //we are inserting to some other location
            int currentIndex = 0;
            Node* previous = NULL;
            Node* current = first;

            for (int count = 0; count < index; count++)
            {
                //traverse the list to the specified location
                previous = current;
                current = (*current).getNextNode();
            }

            //now we want to make the 'previous' node point to the node
            //that we are inserting, and the node that we're inserting
            //point to the 'current' node
            (*previous).setNextNode(nodeToInsert);
            (*nodeToInsert).setNextNode(current);
        }
    }
    else if (index == size)
    {
        //in this case we are 'inserting' to the end of the list, which is
        //what 'append' already does for us, so we can just call the
        //append() function
        append(nodeToInsert);

    }
}

void List::deleteNodeAtIndex(int index)
{
    if ((index < size) && (index >= 0)) //make sure we have a valid index
    {
        //decrement our size counter
        size--;

        if (index == 0)
        {
            //we are deleting the first node in the list
            if (size == 0)
            {
                //we are deleting the *only* node in the list
                delete first;
                first = NULL;
                last = NULL;
            }
            else
            {
                //there are other nodes, so we have to keep the list
                //intact
                Node* temp = first;
                first = (*first).getNextNode();
                delete temp;
            }
        }
        else
        {
            //we need to find the specified index
            Node* current = first;
            Node* previous = NULL;

            for (int count = 0; count < index; count++)
            {
                //traverse to the specified position in the list
                previous = current;
                current = (*current).getNextNode();
            }

            //now we can adjust the list and make our deletion
            if (index != size)
            {
            	(*previous).setNextNode((*current).getNextNode());
			}
			else
			{
				last = previous;
				(*previous).setNextNode(NULL);
			}
            delete current;
        }
    }
}

Node* List::getNodeAtIndex(int index)
{
    if ((index < size) && (index >= 0))  //make sure we have a valid index
    {
        Node* current = first;
        for(int count = 0; count < index; count++)
        {
            //traverse to the desired spot
            current = (*current).getNextNode();
        }
        return current;
    }
    else
    {
        //an invalid index was specified, but we still need to return
        //something, so return NULL

        return NULL;
    }
}

List::~List()
{
	Node* current = first;
	while (current != NULL)
	{
		Node* temp = current;
		current = (*current).getNextNode();
		delete temp;
	}
}

int List::getSize() //returns the size of the list
{
    return size;
}
//END LIST CLASS


//BEGIN STACK CLASS
class Stack
{
private:
	List list;
	int size;

public:
	Stack(); //constructor

	//utility functions
	void push(string data);
	string pop();
	string peek();
	int getSize();
};

Stack::Stack()
{
	//default constructor
	list = List();
	size = 0;
}

int Stack::getSize()
{
	//returns the size of the stack
	return size;
}

void Stack::push(string data)
{
	//push a value on to the stack
	size++;
	list.append(new Node(data, NULL));
}

string Stack::peek()
{
	//read the value at the top of stack, but do not remove it
	string retVal;

	if (list.getNodeAtIndex(size - 1) != NULL)
		retVal = list.getNodeAtIndex(size - 1)->getData();
	else
		retVal = "NULL"; //"NULL" is returned as an error code

	return retVal;
}

string Stack::pop()
{
	//read and remove the value at the top of the stack
	string retVal;

	if (list.getNodeAtIndex(size - 1) != NULL)
	{
		retVal = list.getNodeAtIndex(size - 1)->getData();
		list.deleteNodeAtIndex(size - 1);
		size--;
	}
	else
		retVal = "NULL";  //"NULL" is returned as an error code

	return retVal;
}
//END STACK CLASS


int main()
{
   //test the Stack a little bit
   Stack s = Stack();

   s.push("1");
   s.push("2");
   s.push("3");

   //should print:
   //3
   //2
   //2
   //2
   //1
   //NULL
   //done
   cout << s.pop() <<endl;
   cout << s.peek() <<endl;
   cout << s.peek() <<endl;
   cout << s.pop() <<endl;
   cout << s.pop() <<endl;
   cout << s.pop() <<endl;
   s.pop();
   s.pop();
   s.pop();
   cout << "done" <<endl;

   return 0;
}
