#include "Action.h"
#include "Main.h"

Action::Action()
{
	sActionCount += 1;
}


bool Go::DoAction()
{
	//Checks if all of current rooms exits name is typed in
	if (Main::Get()->mParser.First(Main::Get()->mInput, "go", "move", "walk")
		|| Main::Get()->mParser.First(Main::Get()->mInput, "w", "e", "s")
		|| Main::Get()->mParser.First(Main::Get()->mInput, "n", "west", "north")
		|| Main::Get()->mParser.First(Main::Get()->mInput, "east", "south"))
	{
		Main* main = Main::Get();

		for (list<Exit*>::iterator i = main->mCurrentRoom->mExits.begin(); i != main->mCurrentRoom->mExits.end(); ++i)
		{
			//Make is so you can go with w, e, s, n
			if (main->mParser.First(main->mInput, "w"))
				main->mInput = "west";
			else if (main->mParser.First(main->mInput, "n"))
				main->mInput = "north";
			else if (main->mParser.First(main->mInput, "e"))
				main->mInput = "east";
			else if (main->mParser.First(main->mInput, "s"))
				main->mInput = "south";


			if (main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				//Moves room
				main->mCurrentRoom->Move(*i);
				return true;
			}
		}

		main->mOut.Text("You can not go there.");
		return true;
	}

	return false;
}

bool Take::DoAction()
{
	//If you type take/get/pick, loop through current rooms items, checking if you typed in their names
	if (Main::Get()->mParser.First(Main::Get()->mInput, "take", "get", "pick"))
	{
		Main* main = Main::Get();

		for (list<Item*>::iterator i = main->mCurrentRoom->mItems.begin(); i != main->mCurrentRoom->mItems.end(); ++i)
			if (main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				if ((*i)->GetType() == eTake)
				{
					(*i)->TakeItemDescriptions();

					//Check if it is held by an item (ItemHolder), then take it out
					for (list<Item*>::iterator h = main->mCurrentRoom->mItems.begin(); h != main->mCurrentRoom->mItems.end(); ++h)
						if ((*h)->GetType() == eHold)
						{
							ItemHolder* temp = (ItemHolder*)(*h);
							if (temp->GetHeld() == (*i))
							{
								temp->RemoveItem();
								break;
							}
						}

					main->mInventory.splice(main->mInventory.end(), main->mCurrentRoom->mItems, i);
					
					return true;
				}

				//Can't take it, but can still show it's can't take description
				main->mOut.Text((*i)->GetTakeDescription());
				main->mInput = "";
				return true;
			}

		main->mOut.Text("You can not take that.");
		main->mInput = "";
		return true;
	}

	return false;
}

bool CheckInventory::DoAction()
{
	if (Main::Get()->mParser.First(Main::Get()->mInput, "i", "inventory"))
	{
		Main* main = Main::Get();

		int count = 0;
		main->mOut.Text("                Inventory: ");
		main->mOut.EndLines(1);
		main->mOut.Text("*-------------------------------------------*");
		for (list<Item*>::iterator i =main->mInventory.begin(); i != main->mInventory.end(); ++i)
		{
			main->mOut.EndLines(1);
			main->mOut.Int(count);
			main->mOut.Text(". " + (*i)->GetName());

			++count;
		}
		main->mOut.EndLines(1);
		main->mOut.Text("*-------------------------------------------*");

		main->mInput = "";
		return true;
	}

	return false;
}

bool Talk::DoAction()
{
	if (Main::Get()->mParser.First(Main::Get()->mInput, "talk", "chat", "converse"))
	{
		for (list<NPC*>::iterator i = Main::Get()->mCurrentRoom->mNPCs.begin(); i != Main::Get()->mCurrentRoom->mNPCs.end(); ++i)
			if (Main::Get()->mParser.Next(Main::Get()->mInput, (*i)->GetNPCName()))
			{
				Main::Get()->mSaveLoad.AddAction(Main::Get()->mInput);
				(*i)->Talk();

				return true;
			}

		Main::Get()->mOut.Text("Talking to yourself will not help you.");
		Main::Get()->mInput = "";
		return true;
	}

	return false;
}

bool Save::DoAction()
{
	if (Main::Get()->mParser.First(Main::Get()->mInput, "save"))
	{
		Main* main = Main::Get();

		//SAVE FILES *MUST* HAVE SAME NAME AND HAVE 1 2 3 4... AT THE END!!!
		string fileName = "File0";
		main->mSaveLoad.LoadSaveSlot("File0");

		//Pick the save slot
		main->mOut.Text("Choose a slot number to save");
		main->GetInput();

		//Check if you input a valid save slot
		if (main->mParser.First(main->mInput, "1", "2", "3"))
		{
			string slotNumber = main->mInput;

			main->mOut.Text("This will overwrite previous save. Continue? (y/n)");
			main->GetInput();

			//Are you sure?
			if (main->mParser.First(main->mInput, "y"))
			{
				fileName += slotNumber;
				main->mSaveLoad.SaveGame(fileName);

				main->mOut.Text("Game Saved!");
				main->ClearScreenWait();
				main->mInput = "";
				return true;
			}

			main->mOut.Text("Game was not saved.");
			main->ClearScreenWait();
			main->mInput = "";
			return true;
		}

		main->mOut.Text("Invalid slot number, game has not been saved!");
		main->ClearScreenWait();
		main->mInput = "save";
		return true;
	}

	return false;
}

bool Load::DoAction()
{
	if (Main::Get()->mParser.First(Main::Get()->mInput, "load"))
	{
		Main* main = Main::Get();

		//SAVE FILES *MUST* HAVE SAME NAME AND HAVE 1 2 3 4... AT THE END!!!
		string fileName = "File0";
		main->mSaveLoad.LoadSaveSlot("File0");

		//Pick the load slot
		main->mOut.Text("Choose a slot number to load");
		main->GetInput();

		//Check if you input a valid save slot
		if (main->mParser.First(main->mInput, "1", "2", "3"))
		{
			string slotNumber = main->mInput;

			main->mOut.Text("Any unsaved progress will be lost. Continue? (y/n)");
			main->GetInput();

			//Are you sure?
			if (main->mParser.First(main->mInput, "y"))
			{
				fileName += slotNumber;
				main->mSaveLoad.LoadGame(fileName);

				main->ClearScreenWait();
				main->mInput = "";
				return true;
			}

			main->mOut.Text("Game was not loaded.");
			main->ClearScreenWait();
			main->mInput = "";
			return true;
		}

		main->mOut.Text("Invalid slot number, game has not been loaded!");
		main->ClearScreenWait();
		main->mInput = "load";
		return true;
	}

	return false;
}

bool Use::DoAction()
{
	//If you type use, loop through inventory, checking if you typed in their names
	if (Main::Get()->mParser.First(Main::Get()->mInput, "use"))
	{
		Main* main = Main::Get();

		for (list<Item*>::iterator i = main->mInventory.begin(); i != main->mInventory.end(); ++i)
			if (main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				//Check if you also typed in what to use it on
				for (list<Item*>::iterator r = main->mCurrentRoom->mItems.begin(); r != main->mCurrentRoom->mItems.end(); ++r)
					if (main->mParser.Next(main->mInput, (*r)->GetName()))
					{
						//Use on a holder item
						if ((*r)->GetType() == eHold)
						{
							main->mOut.Text("You used the " + (*i)->GetName() + " on the " + (*r)->GetName() + "!");

							ItemHolder* tempHolder = (ItemHolder*)(*r);

							//Check if it already has an item first
							if (tempHolder->AddItem(*i))
								main->mCurrentRoom->mItems.splice(main->mCurrentRoom->mItems.end(), main->mInventory, i);

							main->mCurrentRoom->UpdateExits(); //This is for checking if holder items are holding the right items for ExitIfItems
							return true;
						}

						//Use on a use item
						else if ((*r)->GetType() == eUse)
						{
							((ItemUse*)(*r))->CheckUse(*i);
							return true;
						}
					}

					main->mOut.Text("You can not use that item on that.");
					return true;
			}

		main->mOut.Text("You can not use what you do not have.");
		return true;
	}

	return false;
}

bool Check::DoAction()
{
	//Goes through all items in room, and the inventory, checking you you are checking that item
	if (Main::Get()->mParser.First(Main::Get()->mInput, "check", "look", "examine"))
	{
		Main* main = Main::Get();

		//Rooms items
		for (list<Item*>::iterator i = main->mCurrentRoom->mItems.begin(); i != main->mCurrentRoom->mItems.end(); ++i)
			if (main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				main->mOut.Text((*i)->GetDescription());
				main->mInput = "";
				return true;
			}

		//Inventory items
		for (list<Item*>::iterator i = main->mInventory.begin(); i != main->mInventory.end(); ++i)
			if (main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				main->mOut.Text((*i)->GetDescription());
				main->mInput = "";
				return true;
			}

		//Room
		if (main->mParser.Next(main->mInput, "room"))
		{
			Main::Get()->ClearScreen();
			main->mInput = "";
			return true;
		}

		//Don't find anything
		main->mOut.Text("You can not check that");
		main->mInput = "";
		return true;
	}

	return false;
}

bool Restart::DoAction()
{
	//Goes through all items in room, and the inventory, checking you you are checking that item
	if (Main::Get()->mParser.First(Main::Get()->mInput, "restart", "reset"))
	{
		Main* main = Main::Get();
		
		main->mOut.Text("Restarting the game will not save any unsaved progress. Continue? (y/n)");
		main->GetInput();

		//Are you sure?
		if (main->mParser.First(main->mInput, "y"))
		{
			main->Reset(1);

			main->mInput = "";
			return true;
		}

		main->mOut.Text("Game was not loaded. Resuming game.");
		main->ClearScreenWait();
		main->mInput = "";
		return true;
	}

	return false;
}

bool Verbose::DoAction()
{
	//Goes through all items in room, and the inventory, checking you you are checking that item
	if (Main::Get()->mParser.First(Main::Get()->mInput, "verbose"))
	{
		Main* main = Main::Get();
		
		main->mOut.Text("Change rooms' description mode to verbose? (y/n)");
		main->GetInput();

		//Are you sure?
		if (main->mParser.First(main->mInput, "y"))
		{
			main->mDesMode = eVerbose;

			main->mOut.Text("Verbose descriptions selected!");
			main->ClearScreenWait();
			main->mInput = "";
			return true;
		}

		main->mOut.Text("Nothing was changed. Resuming game.");
		main->ClearScreenWait();
		main->mInput = "";
		return true;
	}

	return false;
}

bool Brief::DoAction()
{
	//Goes through all items in room, and the inventory, checking you you are checking that item
	if (Main::Get()->mParser.First(Main::Get()->mInput, "brief"))
	{
		Main* main = Main::Get();
		
		main->mOut.Text("Change rooms' description mode to brief? (y/n)");
		main->GetInput();

		//Are you sure?
		if (main->mParser.First(main->mInput, "y"))
		{
			main->mDesMode = eBrief;

			main->mOut.Text("Brief descriptions selected!");
			main->ClearScreenWait();
			main->mInput = "";
			return true;
		}

		main->mOut.Text("Nothing was changed. Resuming game.");
		main->ClearScreenWait();
		main->mInput = "";
		return true;
	}

	return false;
}

bool Quit::DoAction()
{
	//Goes through all items in room, and the inventory, checking you you are checking that item
	if (Main::Get()->mParser.First(Main::Get()->mInput, "quit"))
	{
		Main* main = Main::Get();
		
		main->mOut.Text("Are you sure you want to quit? Who knows what is around that next corner! (y/n)");
		main->GetInput();

		//Are you sure?
		if (main->mParser.First(main->mInput, "y"))
		{
			main->QuitGame();

			main->mOut.Text("Come back anytime.");
			main->ClearScreenWait();
			main->mInput = "";
			return true;
		}

		main->mOut.Text("Quit aborted. Resuming game.");
		main->ClearScreenWait();
		main->mInput = "";
		return true;
	}

	return false;
}

bool ShowScore::DoAction()
{
	if (Main::Get()->mParser.First(Main::Get()->mInput, "score"))
	{
		Main::Get()->mOut.Text("Score: ");
		Main::Get()->mOut.Int(Main::Get()->GetScore());

		Main::Get()->mOut.Text(" of ");
		Main::Get()->mOut.Int(Main::Get()->GetMaxScore());

		Main::Get()->mInput = "";
		return true;
	}

	return false;
}

bool ShowTurns::DoAction()
{
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "turns"))
	{
		Main::Get()->mOut.Text("Turns: ");
		Main::Get()->mOut.Int(Main::Get()->GetTurn());

		Main::Get()->mInput = "";
		return true;
	}

	return false;
}

bool Push::DoAction()
{
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "push"))
	{
		Main* main = Main::Get();

		//Check if there is a pushable item and you typed its name
		for (list<Item*>::iterator i = main->mCurrentRoom->mItems.begin(); i != main->mCurrentRoom->mItems.end(); ++i)
			if ((*i)->GetType() == ePush && main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				RoomPuzzleBox* tempRoom = (RoomPuzzleBox*)main->mCurrentRoom;

				if (main->mParser.Next(main->mInput, "north", "up"))
				{
					tempRoom->Push(0, -1);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "south", "down"))
				{
					tempRoom->Push(0, 1);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "west", "left"))
				{
					tempRoom->Push(-1, 0);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "east", "right"))
				{
					tempRoom->Push(1, 0);
					return true;
				}

				main->mOut.Text("Where exactly are you trying to push that " + (*i)->GetName() + "?");
				main->mInput = "";
				return true;

			}

			main->mOut.Text("What exactly are you trying to push?");
			main->mInput = "";
			return true;
	}

	return false;
}

bool Pull::DoAction()
{
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "pull"))
	{
		Main* main = Main::Get();

		//Check if there is a pushable item and you typed its name
		for (list<Item*>::iterator i = main->mCurrentRoom->mItems.begin(); i != main->mCurrentRoom->mItems.end(); ++i)
			if ((*i)->GetType() == ePush && main->mParser.Next(main->mInput, (*i)->GetName()))
			{
				RoomPuzzleBox* tempRoom = (RoomPuzzleBox*)main->mCurrentRoom;

				if (main->mParser.Next(main->mInput, "north", "up"))
				{
					tempRoom->Pull(0, -1);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "south", "down"))
				{
					tempRoom->Pull(0, 1);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "west", "left"))
				{
					tempRoom->Pull(-1, 0);
					return true;
				}
				else if (main->mParser.Next(main->mInput, "east", "right"))
				{
					tempRoom->Pull(1, 0);
					return true;
				}

				main->mOut.Text("Where exactly are you trying to pull that " + (*i)->GetName() + "?");
				main->mInput = "";
				return true;
			}

			main->mOut.Text("*Ahem* Pull What?");
			main->mInput = "";
			return true;
	}

	return false;
}

bool Suicide::DoAction()
{
	//Check if you suicide
	if (Main::Get()->mParser.First(Main::Get()->mInput, "suicide"))
	{
		Main* main = Main::Get();
		
		main->mOut.Text("I would advise against that if I were you... (y/n)");
		main->GetInput();

		//Are you sure?
		if (main->mParser.First(main->mInput, "y"))
		{
			main->mOut.Text("Well, I would call that GAME OVER! You should probably quit or restart the game at this point");
			main->GetInput();

			//Infinite loop until you restart or quit
			while (!main->mParser.First(main->mInput, "restart") || !main->mParser.First(main->mInput, "quit"))
			{
				if (main->mParser.First(main->mInput, "restart"))
				{
					main->Reset(1);

					main->mOut.Text("Thats right, suicide can't stop you. You just get up and try again.");
					main->ClearScreenWait();
					main->mInput = "";
					return true;
				}
				else if (main->mParser.First(main->mInput, "quit"))
				{
					main->QuitGame();

					main->mOut.Text("You kill yourself and give up? How embarrasing.");
					main->ClearScreenWait();
					main->mInput = "";
					return true;
				}

				main->GetInput();
			}
		}

		main->mOut.Text("Good man. Take a deep breath, walk around, and sit back down.");
		main->ClearScreenWait();
		main->mInput = "";
		return true;
	}

	return false;
}

bool Profanity::DoAction()
{
	//Checks if you cuss
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "fuck", "damn", "shit")
		|| Main::Get()->mParser.Next(Main::Get()->mInput, "suck", "hate"))
	{
		
		Main::Get()->mOut.Text("I don't appreciate that language; you better stop or I will erase your game.");

		Main::Get()->mInput = "";
		return true;
	}

	return false;
}

bool ClearScreen::DoAction()
{
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "clear", "cls"))
	{
		Main::Get()->ClearScreen();
		Main::Get()->mInput = "";
		return true;
	}

	return false;
}

bool Help::DoAction()
{
	if (Main::Get()->mParser.Next(Main::Get()->mInput, "help", "?"))
	{
		Main::Get()->mOut.ClearScreen();
		Main::Get()->mHelp->Talk();
		return true;
	}

	return false;
}


ActionList::ActionList()
{
	sActionCount = 0;

	Go* go			= new Go();
	Take* take		= new Take();
	CheckInventory* checkInventory = new CheckInventory();
	Talk* talk		= new Talk();
	Save* save		= new Save();
	Load* load		= new Load();
	Use* use		= new Use();
	Check* check	= new Check();
	Restart* restart= new Restart();
	Verbose* verbose= new Verbose();
	Brief* brief	= new Brief();
	Quit* quit		= new Quit();
	ShowScore* showScore = new ShowScore();
	ShowTurns* showTurns = new ShowTurns();
	Push* push		= new Push();
	Pull* pull		= new Pull();
	Suicide* suicide= new Suicide();
	Profanity* profanity = new Profanity();
	ClearScreen* clearScreen = new ClearScreen();
	Help* help		= new Help();

	mActionList = new IAction*[sActionCount];
	memset(mActionList, 0, sizeof(IAction*) * sActionCount);

	int i = 0;
	mActionList[i++] = go;
	mActionList[i++] = take;
	mActionList[i++] = checkInventory;
	mActionList[i++] = talk;
	mActionList[i++] = save;
	mActionList[i++] = load;
	mActionList[i++] = use;
	mActionList[i++] = check;
	mActionList[i++] = restart;
	mActionList[i++] = verbose;
	mActionList[i++] = brief;
	mActionList[i++] = quit;
	mActionList[i++] = showScore;
	mActionList[i++] = showTurns;
	mActionList[i++] = push;
	mActionList[i++] = pull;
	mActionList[i++] = suicide;
	mActionList[i++] = profanity;
	mActionList[i++] = clearScreen;
	mActionList[i++] = help;
}

ActionList::~ActionList()
{	
	for (int i = 0; i < sActionCount; ++i)
	{
		delete mActionList[i];
		mActionList[i] = 0;
	}

	delete [] mActionList;
	
}

void ActionList::CheckActions()
{
	//Goes through all the actions in the list, checking if you hit a valid action
	for (int i = 0; i < sActionCount; ++i)
	{
		if (mActionList[i]->DoAction())
		{
			//Adds the valid action to the list of saved actions (used to save/load the game)
			if (Main::Get()->mInput != "")
				Main::Get()->mSaveLoad.AddAction(Main::Get()->mInput);

			return;
		}
	}

	//Otherwise, input is invalid
	InvalidInput();
}

void ActionList::InvalidInput()
{
	Main::Get()->mOut.Text("Invalid Input.");
}