/************************************************************************/
/*	
*	WZORZEC PROJEKTOWY KOMENDY
*	
*	Piotr Falgowski G1SST
*
*	Opis: 
*
*	Wzorzec KOMENDY jest wzorcem projektowym należącym do wzorców czynnościowym. 
*	Pozwala enkapsulować czynności i ich parametry w postaci obiektów. Dzięki temu 
*	obiekt może być gromadzony i wpuszczany w obieg tak jak inne obiekty. Kluczem do 
*	tego wzoru jest abstrakcyjna klasa Command, w której deklarujemy interfejs do 
*	wykonywania operacji. W najprostszej formie ten interfejs zawiera operację Execute.
*	Konkretne podklasy Command precyzują akcje odbiorcy, tworząc magazyn zadań 
*	i przygotowują narzędzia do wykonywania zadań.
*
*             ____________________               ______________________                
*             | KlientWywolujący |-------------->|   <<Interfejs>>    |                
*             |__________________|               |       Command      |                
*             |__________________|               |____________________|                 
*             |__________________|               |   + execute()=0    |                 
*                                                |____________________|                 
*                                                    /|\        /|\						
*                                       ______________|          |					
*   ______________________       _______|______________      _________|________			
*   |  Klient Odbiorca   |       |  Konkretna Komenda |      |  Inna Komenda  |		
*   |____________________|<------|____________________|      |________________|		
*   |____________________|       |____________________|      |________________|		
*   |____________________|       |  + execute()       |      | + execute()    |			
*                                |____________________|      |________________|		
*
*                                                                                  
*	Wzorzec komendy należy używać wszędzie tam gdy:
*		-	parametryzujesz obiekty poprzez akcje które wykonują,
*		-	precyzujesz, ustawiasz w kolejce i wykonujesz żądania w różnym czasie,
*		-	chcesz mieć możliwość cofać, przywracać akcje,
*		-	chcesz mieć możliwość rejestrowania zmian tak by mogły być one ponownie
*			wykorzystane w przypadku gdy system przestanie działać,
*
*/
/************************************************************************/


// Przyklad użycia wzorca komendy do stworzenia "elektornicznego organizera";) - przyklad cofania i przywracania zmian

#include <iostream>
#include <vector>
#include <string>

using namespace std;


//Klasa - interfejs po której będą dziedziczyć konkretne komendy
class Command{
public:

	// Metoda execute - wykonanie komendy.
	virtual void execute() = 0;
	// Metoda undo - potrzebna gdy chcemy mieć funkcje cofania zmian.
	virtual void undo() = 0;
	virtual ~Command(){};
};

//Klasa addMeeting - konkretyzacja klasy Command - dodaje spotkanie do organizera. 
class addMeeting : public Command {
public:

	// konstruktor przyjmuje argumenty z jakimi ma być później wykonana komenda,
	// oraz adres obiektu na rzecz którego będzie wykonana
	addMeeting(string person, string time, string* doc){
		_person = person;
		_time = time;
		_doc = doc;
	}

	// Metoda execute - dodaje do "organizera" spotaknie, oraz robi jego kopie przed zmianą.
	void execute(){
		_prevDoc = *_doc;
		*_doc += ("Spotkanie z " +_person +" o godzinie: "+ _time + "\n");
	}

	// Metoda undo przywraca "organizer" do stanu sprzed wykonania komendy
	void undo(){
		*_doc = _prevDoc;
	}
private:
	string _person;		// parametr komendy
	string _time;		// parametr komendy
	string _prevDoc;	// kopia obiektu przed wykonaniem komendy
	string* _doc;		// wskaźnik na obiekt na rzecz którego będzie wykonywana komenda
};

// Klasa addActivity -	podobna jak addMeeting również dziedziczy po Command, 
//						wykonuje tylko inną operacje na obiekcie. Struktura pól 
//						i metod jak wyżej.
class addActivity : public Command {
public:
	addActivity(string activity, string time, string* doc){
		_activity = activity;
		_time = time;
		_doc = doc;
	}
	void execute(){
		_prevDoc = *_doc;
		*_doc += (_activity +" o godzinie: "+ _time + "\n");
	}
	void undo(){
		*_doc = _prevDoc;
	}
private:
	string _activity;
	string _time;
	string _prevDoc;
	string* _doc;
};


// Klasa CmdStack reprezentuje wektor komend.
class CmdStack{
public:

	// Metoda add dodaję komendę oraz ją wykonuje.
	void add(Command *c) {
		c->execute();
		commands.push_back(c);
		commands_redo.clear();
	}

	// Metoda undo potrafi cofnąć dokonane zmiany - zmiana w tył.
	void undo(){
		if(commands.size() > 1) {
			commands_redo.push_back(commands[commands.size()-1]);
			commands[commands.size()-1]->undo();
			commands.pop_back();
		}
		else {
			std::cout << "Nie mozna wykonac undo" << std::endl;
		}
	}

	// Metoda redo przywraca stan przed wykonaniem undo - zmiana w przód.
	void redo(){
		if(commands_redo.size() >= 1) {
			commands.push_back(commands_redo[commands_redo.size()-1]);
			commands_redo[commands_redo.size()-1]->execute();
			commands_redo.pop_back();
		}	
		else {
			std::cout << "Nie mozna wykonac redo" << std::endl;
		}
	}
private:
	vector<Command*> commands; //wektor trzymający komendy - zmiany w tył
	vector<Command*> commands_redo; // wektor trzymający komendy - zmiany w przód
};
int main() {
	CmdStack list;
	string plan; // nasz "organizer" ;)

	std::cout<<"Dodajemy spotkanie z Johnem."<<std::endl;
	list.add(new addMeeting("John Shith", "8:00 - 8:45", &plan));

	std::cout<<"Dodajemy spotkanie z Arnoldem."<<std::endl;
	list.add(new addMeeting("Arnold S.", "9:00 - 15:00", &plan));

	std::cout <<"Nasz plan dnia:"<<std::endl;
	std::cout << plan<<endl;

	std::cout <<"Cofamy ostania zmiane w organizerze"<<std::endl;
	list.undo();
	std::cout << "Nasz plan po cofnieciu ostatniej zmiany:"<<endl;
	std::cout << plan<<endl;

	std::cout <<"Przywracamy ostania zmiane w organizerze"<<std::endl;
	list.redo();
	std::cout << "Nasz plan po przywroceniu ostatniej zmiany:"<<endl;
	std::cout << plan<<endl;

	std::cout<<"Dodajemy koszykowke."<<std::endl;
	list.add(new addActivity("Koszykowka", "16:00 - 18:00", &plan));
	std::cout <<"Nasz ostateczny plan dnia:"<<std::endl;
	std::cout << plan<<endl;

	return 0;
}

