/**************************************************
 * main.cpp: This file glues together the contents
 * of the other files together to implement a working
 * game of FreeCell.
 *
 * Comments have been added to the relevant sections.
 */

#include <iostream> // For cout, endl
#include <string>
#include <sstream>
#include <vector>
#include <ctime>     // For time
#include <algorithm> // For swap
#include <cstdlib>   // For rand, srand, exit
#include "parser-utils.h"
#include "common-types.h"
#include "graphics.h"
using namespace std;

/* Randomize()
 * Seeds the randomizer so random shuffling will work
 * correctly.  Look into <cstdlib> and <ctime> for more
 * info on the functions here.
 */
void Randomize()
{
	srand((unsigned int)time(NULL));
}

/**
 * FillDeck: Fills a vector<cardT> with all of the cards in a standard
 * 52-card deck.  The cards are in sorted order.
 */
void FillDeck(vector<cardT>& deck)
{
	for(int suit = 0; suit < NumSuits; ++suit)
		for(int value = 0; value < NumValues; ++value)
			deck.push_back(MakeCard(static_cast<suitT>(suit),
									static_cast<valueT>(value)));
}


/**
 * ShuffleDeck: Shuffles the cards in a deck.  This works
 * by swapping every element in the deck with a random card
 * that precedes it.  This is correct, and it's an interesting
 * exercise to see why.
 */
void ShuffleDeck(vector<cardT>& deck)
{
	for(int i = 0; i < deck.size(); ++i)
	{
		/* The rand() function returns a random value between
		 * 0 and RAND_MAX, inclusive.  By using the modulus
		 * operator, we can restrict this range to between
		 * 0 and i.
		 */
		int j = rand() % (i + 1);

		/* Swap the cards.  Note that this might be swapping a card
		 * with itself, but that's perfectly fine.
		 */
		swap(deck[i], deck[j]);
	}
}

/**
 * DealInitialTableaux: Deals the deck of cards out and sets up
 * the done and free cells.
 */
void DealInitialTableaux(gameT& game, vector<cardT>& deck)
{
	/* Make sure we have enough piles, cells, and done cells. */
	game.piles.resize(kNumPiles);
	game.cells.resize(kNumCells);
	game.done.resize(NumSuits);

	/* Iterate across the piles dealing cards out.  This uses a bit of
	 * modular arithmetic to deal across the rows over and over again:
	 * the first card goes to row 0, the second to row 1, ..., then the
	 * ninth card to row 0, tenth to row 1 again, etc.
	 */
	for(int i = 0; i < deck.size(); ++i)
		game.piles[i % game.piles.size()].push_back(deck[i]);

	/* Set the cells and done pile to be empty. */
	for(int i = 0; i < game.cells.size(); ++i)
		game.cells[i].isFilled = false;
	for(int i = 0; i < game.done.size(); ++i)
		game.done[i].isFilled = false;
}

/**
 * InitializeGame: Configures a game so that it's ready to play.
 */
void InitializeGame(gameT& game)
{
	/* Seed the randomizer. */
	Randomize();

	/* Fill a deck of cards and deal it out. */
	vector<cardT> deck;
	FillDeck(deck);
	ShuffleDeck(deck);
	DealInitialTableaux(game, deck);
}

/* Process_help: Prints out a help message. */
void Process_help(stringstream& parser, gameT& game)
{
	cout << "List of commands: " << endl;

	/* Use the X Macro trick to automatically generate the code for
	 * printing out all of the commands.
	 */
	#define DEFINE_COMMAND(command, desc) cout << "  " << #command << ": " << desc << endl;
	#include "commands.h"
	#undef DEFINE_COMMAND

	/* If the user typed in something other than just 'help,'
	 * let them know that that wasn't technically necessary.
	 */
	if(!IsEndOfInput(parser))
		cout << "(BTW, the syntax is just 'help')" << endl;
	PressEnterToContinue();
}

/* Process_quit: Quits the program, unless the input wasn't just
 * the word 'quit.'
 */
void Process_quit(stringstream& parser, gameT& game)
{
	/* If the user just typed in 'quit,' quit. */
	if(IsEndOfInput(parser))
		exit(0);
		
	/* Otherwise, print out a message about proper syntax. */
	cout << "Syntax: quit" << endl;
	PressEnterToContinue();
}

/* Returns whether it is legal to move a card from the location
 * specifiedby place.  It is assumed that place refers only to
 * cells or piles.
 */
bool IsValidStart(gameT& game, placeT& place)
{
	/* If the location is a cell, we can legally move the card from
	 * there if the cell isn't empty (i.e. the card actually exists)
	 */
	if(place.type == Cell)
		return game.cells[place.index].isFilled;

	/* Otherwise, the location is a pile.  We can move from a pile
	 * only if the pile isn't empty (i.e. the card actually exists)
	 */
	return !game.piles[place.index].empty();
}

/* Given a placeT, returns what card is at the location indicated by
 * that place.  This assumes that the location we're talking about
 * is actually occupied.
 */
cardT CardAt(gameT& game, placeT where)
{
	/* If the location is a cell, return the card contained in the cell. */
	if(where.type == Cell)
		return game.cells[where.index].card;

	/* Otherwise, return the last card in the specified row. */
	return game.piles[where.index].back();
}

/* Returns whether a card is a red card.  This is true if it's a Heart
 * or a Diamond.
 */
bool IsRed(cardT card)
{
	return card.suit == Hearts || card.suit == Diamonds;
}

/* CanPutOnTopOf returns whether it's legal to put the new top card
 * on top of the specified bottom card.
 */
bool CanPutOnTopOf(cardT top, cardT bottom)
{
	/* We can't put top on bottom if they're the same color (they're either
	 * both red or both black.)
	 */
	if(IsRed(top) == IsRed(bottom))
		return false;

	/* It's legal to move the card if the top card's value is one before
	 * the bottom card's value.
	 */
	return top.value + 1 == bottom.value;
}

/* IsValidDest: Returns whether it's legal to move the card
 * at location source into the location specified by dest.
 */
bool IsValidDest(gameT& game, placeT source, placeT dest)
{
	/* If we're moving the card onto a cell, the cell has to
	 * be empty.
	 */
	if(dest.type == Cell)
		return !game.cells[dest.index].isFilled;

	/* Otherwise, we can move the card only if it's legal to put the
	 * card in source onto the card at dest.
	 */
	return CanPutOnTopOf(CardAt(game, source), CardAt(game, dest));
}

/* MoveCard: Given a source and destination, moves the card at location
 * source to the card at location dest.
 */
void MoveCard(gameT& game, placeT source, placeT dest)
{
	/* First, based on what the destination is, put the card there.
	 * If it's a cell or the done pile, we just fill the cell with the
	 * card.
	 */
	if(dest.type == Cell)
	{
		game.cells[dest.index].isFilled = true;
		game.cells[dest.index].card = CardAt(game, source);
	}
	else if(dest.type == Done)
	{
		game.done[dest.index].isFilled = true;
		game.done[dest.index].card = CardAt(game, source);
	}
	/* Otherwise, if it's a pile, we just append it to the row. */
	else if(dest.type == Pile)
		game.piles[dest.index].push_back(CardAt(game, source));

	/* Now we need to get rid of the card from where it used to be.
	 * If that's a cell, we just say that the cell is empty.
	 * Otherwise, if it's a row, we pop the card off the row.
	 */
	if(source.type == Cell)
		game.cells[source.index].isFilled = false;
	else
		game.piles[source.index].pop_back();
}

/* CanLift returns whether we can lift the card into the proper done pile. */
bool CanLift(gameT& game, cardT card)
{
	/* If there's already a card in the done pile, we need to check
	 * that the card's value is the next in the sequence.
	 */
	if(game.done[card.suit].isFilled)
		return game.done[card.suit].card.value + 1 == card.value;
	/* Otherwise, if the cell is empty, then the source card has to be an
	 * ace.
	 */
	else
		return card.value == Ace;
}

/* Process_move: Handles a move command. */
void Process_move(stringstream& parser, gameT& game)
{
	/* This next bit's tricky.  We need to read the locations specified
	 * by the user out of the stream, so we chain together multiple calls
	 * to ParseLocation together and then call IsEndOfInput to assert
	 * that there wasn't anything else there.  The use of the && operator
	 * and short-circuiting forces the operations to occur in the right
	 * order.
	 */
	placeT source, dest;
	if(ParseLocation(parser, source) && ParseLocation(parser, dest) && IsEndOfInput(parser))
	{
		/* If the specified start location isn't valid, report this to
		 * the user.
		 */
		if(!IsValidStart(game, source))
		{
			cout << "Invalid start location." << endl;
			PressEnterToContinue();
		}
		/* Otherwise, if the destination isn't good, report that. */
		else if(!IsValidDest(game, source, dest))
		{
			cout << "Invalid destination." << endl;
			PressEnterToContinue();
		}
		/* Otherwise, move the card! */
		else
			MoveCard(game, source, dest);

		return;

	}

	/* Print a message about good syntax. */
	cout << "Syntax: move source dest" << endl;
	PressEnterToContinue();
}

/* Moves a card to its final destination. */
void Process_lift(stringstream& parser, gameT& game)
{
	/* As with Process_move, this code parses the input from the stream
	 * and doesn't move things unless the input was valid.
	 */
	placeT source;
	if(ParseLocation(parser, source) && IsEndOfInput(parser))
	{
		/* If there's nothing to move, report an error. */
		if(!IsValidStart(game, source))
		{
			cout << "Invalid location." << endl;
			PressEnterToContinue();
		}
		/* If this card isn't the next in the sequence, that's also
		 * a problem.
		 */
		else if(!CanLift(game, CardAt(game, source)))
		{
			cout << "That isn't the next card in the sequence." << endl;
			PressEnterToContinue();
		}
		/* Otherwise, move the card up! */
		else
			MoveCard(game, source, MakePlace(Done, CardAt(game, source).suit));
		return;
	}

	/* Otherwise print a syntax error. */
	cout << "Syntax: lift source" << endl;
	PressEnterToContinue();
}

/**
 * ProcessCommand: Prompts the user for a command and updates the
 * game state appropriately.
 */
void ProcessCommand(gameT& game)
{
	/* Get the input from the user and store it in a stringstream. */
	cout << "Enter command: ";
	stringstream parser(ConvertToLowerCase(GetLine()));

	/* Read out the very first token.  This should be the command name. */
	string input;
	parser >> input;

	/* Use the X Macro trick to automatically generate the dispatch
	 * code to call the right Process_* function.  Notice that the
	 * name of the function to call is Process_##command.  This uses
	 * the token-pasting operator (##) to glue together Process_
	 * and the name of the command to form a single function name.
	 * For example, if we have DEFINE_COMMAND(help, "..."), this would
	 * form the string Process_help.
	 */
	#define DEFINE_COMMAND(command, desc) \
	if(input == #command) \
	{ \
		Process_##command(parser, game); \
		return; \
	}
	#include "commands.h"
	#undef DEFINE_COMMMAND
	
	/* If we got here, we have no idea what command the user entered and
	 * can present a message to that effect.
	 */
	cout << "Unknown command, type 'help' for a list of commands." << endl;
	PressEnterToContinue();
}

/**
 * main: Sets up and runs the FreeCell game.
 */
int main()
{
	/* Create and set up the game. */
	gameT game;
	InitializeGame(game);

	while(true)
	{
		/* Print the current game state. */
		PrintState(game);

		/* Process a command from the user. */
		ProcessCommand(game);
	}

	return 0;
}
