#ifndef UNDO_REDO_STACK_H
#define UNDO_REDO_STACK_H

/*****************************************
 * UW User ID:  pagunnew
 * Submitted for ECE 250
 * Semester of Submission:  Winter 2011
 *
 * By submitting this file, I affirm that
 * I am the author of all modifications to
 * the provided  code.
 *****************************************/

#include "ece250.h"
#include "Single_list.h"
#include "Exception.h"

template <typename Object>
class Undo_redo_stack {
	private:
		Single_list<Object> undo_stack;
		Single_list<Object> redo_stack;

	public:
		bool can_undo() const;
		bool can_redo() const;

		void event( const Object & );
		Object undo();
		Object redo();
		void clear();

    // Friends
	template <typename T>
	friend std::ostream &operator << ( std::ostream &out, const Undo_redo_stack<T> &stack );
};

template <typename Object>
bool Undo_redo_stack<Object>::can_undo() const {
    // Returns true if the undo stack is not empty
    if (undo_stack.empty())
    {
        return false;
    }
    else
    {
        return true;
    }
}

template <typename Object>
bool Undo_redo_stack<Object>::can_redo() const {
    // Returns true if the redo stack is not empty
    if (redo_stack.empty())
    {
        return false;
    }
    else
    {
        return true;
    }
}

template <typename Object>
void Undo_redo_stack<Object>::event( const Object & obj ) {
    // Push the element onto the top of the undo stack and empty the redo stack
    undo_stack.push_front(obj);

    while(!redo_stack.empty())
    {
        redo_stack.pop_front();
    }
}

template <typename Object>
Object Undo_redo_stack<Object>::undo() {
    // Pop the top element off of the undo stack, push it onto the redo stack, and return the object
    Object temp = undo_stack.front();
    redo_stack.push_front(temp);
    undo_stack.pop_front();
	return temp;
}

template <typename Object>
Object Undo_redo_stack<Object>::redo() {
    // Pop the top element off of the redo stack, push it onto the undo stack, and return the object
    Object temp = redo_stack.front();
    undo_stack.push_front(temp);
    redo_stack.pop_front();
	return temp;
}

template <typename Object>
void Undo_redo_stack<Object>::clear() {
    // Removes all the elements in the redo stack
    while(!redo_stack.empty())
    {
        redo_stack.pop_front();
    }

    // Removes all the elements in the undo stack
    while(!undo_stack.empty())
    {
        undo_stack.pop_front();
    }
}


// You can modify this function however you want:  it will not be tested
template <typename T>
std::ostream &operator << ( std::ostream &out, const Undo_redo_stack<T> &stack ) {
	// write code to output stack
	out << "Undo:";
	for ( Single_node<T> *ptr = stack.undo_stack.head(); ptr != 0; ptr = ptr->next() ) {
		out << " -> " << ptr->retrieve();
	}
    out << std::endl;

	out << "Redo:";
    for ( Single_node<T> *ptr = stack.redo_stack.head(); ptr != 0; ptr = ptr->next() ) {
		out << " -> " << ptr->retrieve();
	}

	return out;
}


#endif

