#include "GameBot.h"

#include "Common.h"
#include <stdio.h>
#include "SynAnalyzer.h"

const String COMMAND_MARKET = "market";
const String COMMAND_TURN = "turn";
const String COMMAND_INFO = "info";
const String COMMAND_CREATE = ".create";
const String COMMAND_JOIN = ".join";
const String COMMAND_START = "start";
const String COMMAND_PROD = "prod";
const String COMMAND_BUY = "buy";
const String COMMAND_SELL = "sell";
const String COMMAND_SAY = "say";

const String ANSWER_MARKET = "MARKET";
const String ANSWER_ENDTURN = "ENDTURN";
const String ANSWER_JOIN = "JOIN";
const String ANSWER_LEFT = "LEFT";
const String ANSWER_START = "START";
const String ANSWER_BANKRUPT = "BANKRUPT";
const String ANSWER_PLAYERS = "PLAYERS";

/*Connection*/
void Connection::Connect(const String host, const unsigned port) const
{
	try
	{
		socket.Connect(host, port);
	}
	catch (ClientSocket::Exception)
	{
		throw ConnectError();
	}
}

void Connection::SendCommand(const String command) const
{
	/*Printing the command for debug*/
	printf("  > %s\n", command.CStr());

	socket.Send(command + "\n");
}

String Connection::getLine()
{
	String::SizeType from = 0, p;
	while ((p = buffer.Find('\n', from)) >= buffer.GetLength())
	{
		from = buffer.GetLength();
		buffer += socket.Receive();
	}

	String result = buffer.SubString(0, p - 1);
	buffer.Cut(0, p);

	/*Printing the line for debug*/
	printf("%s\n", result.CStr());

	return result;
}

Answer Connection::WaitAnswer(const Answer::Type type, const String name)
{
	do
	{
		Answer answer(getLine());
		if (answer.GetType() == type && (name == "" || answer.GetName() == name)) return answer;
	} while (true);
}

Answer Connection::NextAnswer()
{
 return Answer(getLine());
}

/*GameBot*/
GameBot::GameBot(const String name)
{
	this->name = name;
}

void GameBot::Connect(const String host, const unsigned port) const
{
	try
	{
		connection.Connect(host, port);
	}
	catch (Connection::ConnectError)
	{
		throw ConnectError();
	}

	connection.SendCommand(name);
}

void GameBot::Join(const unsigned gameID)
{
	if (gameID == 0)
	{
		/*Here we must get the id of any game or wait for it...*/
	}

	connection.SendCommand(COMMAND_JOIN + " " + ToString(gameID));
}

void GameBot::Create(const unsigned players)
{
	connection.SendCommand(COMMAND_CREATE);
	unsigned count = 0;
	while (count < players)
	{
		Answer answer = connection.WaitAnswer(Answer::at_system);
		if (answer.GetName() == ANSWER_JOIN)	count++;
			else if (answer.GetName() == ANSWER_LEFT) count--;
	}
	connection.SendCommand(COMMAND_START);
}

void GameBot::buy(const unsigned count, const unsigned price)
{
	connection.SendCommand(COMMAND_BUY + " " + ToString(count) + " " + ToString(price));

}

void GameBot::sell(const unsigned count, const unsigned price)
{
	connection.SendCommand(COMMAND_SELL + " " + ToString(count) + " " + ToString(price));
}

void GameBot::prod(const unsigned count)
{
	connection.SendCommand(COMMAND_PROD + " " + ToString(count));
}

void GameBot::say(const String &what)
{
	connection.SendCommand(COMMAND_SAY + " " + what);
}

bool GameBot::turn(GameResult *result)
/*
Returns false if the game is over.
In this case, $result contain the game result.
*/
{
	connection.SendCommand(COMMAND_TURN);

  Answer tmp = connection.WaitAnswer(Answer::at_game);
  /*It will be good to make waiting of several commands in one time...*/
  while (tmp.GetName() != ANSWER_BANKRUPT && tmp.GetName() != ANSWER_ENDTURN)
  	tmp = connection.WaitAnswer(Answer::at_game);

	bool alive = true;
	unsigned count = players.GetCount();
	while (tmp.GetName() == ANSWER_BANKRUPT)
	{
		/*Anybody is bankrupt?*/
		count--;
		if (tmp.GetParams()[0] == name) alive = false; /* Oops, me :) */

		tmp = connection.WaitAnswer(Answer::at_game);
	}

	if (!alive)
	{
		if (count > 0)
		{
			if (result) *result = gr_lose;
		}
		else
			if (result) *result = gr_draw;

		return false;
	}
	else
		if (count == 1)
		{
			if (result) *result = gr_win;
			return false;
		}

	if (tmp.GetName() != ANSWER_ENDTURN)
		connection.WaitAnswer(Answer::at_game, ANSWER_ENDTURN);

	return true;
}

GameBot::GameResult GameBot::Play(FILE *script)
/*
Returns the game result.
*/
{
	Interpreter interpreter(this);
	SynAnalyzer analyzer(script, &interpreter);
	analyzer.Analyze();

	connection.WaitAnswer(Answer::at_game, ANSWER_START);

	GameResult result;
	while (true)
	{
		players.Receive(connection);
		market.Receive(connection);

		interpreter.Evaluate();
		interpreter.Clear();

		if (!turn(&result)) return result;
	}
}

/*Market*/
void Market::Receive(Connection &connection)
{
	connection.SendCommand(COMMAND_MARKET);
	Answer answer = connection.WaitAnswer(Answer::at_game, ANSWER_MARKET);
	const Vector<String> &params = answer.GetParams();
	rawCount = ToUnsigned(params[0]);
	rawPrice = ToUnsigned(params[1]);
	prodCount = ToUnsigned(params[2]);
	prodPrice = ToUnsigned(params[3]);
}

/*Players*/
void Players::Receive(Connection &connection)
{
	connection.SendCommand(COMMAND_INFO);
	Answer answer = connection.WaitAnswer(Answer::at_game, ANSWER_PLAYERS);
	const Vector<String> &params = answer.GetParams();
	count = ToUnsigned(params[0]);
}

/*Interpreter*/
Interpreter::Interpreter(GameBot *bot): current(poliz.Begin())
{
	this->table = new VarsTable;
	this->stack = new PolizStack;
	this->bot = bot;	
}

Interpreter::~Interpreter()
{
	delete table;
	delete stack;
}

void Interpreter::Append(PolizElement *element)
{
	poliz.PushBack(element);
}

void Interpreter::Clear()
{
	delete table;
	delete stack;
	this->table = new VarsTable;
	this->stack = new PolizStack;
}

void Interpreter::Evaluate()
{
	current = poliz.Begin();
	while (current != poliz.End())
	{
		printf("\n[Interpreter] New poliz\nStack:\n");
		for (PolizStack::Iterator i = stack->Begin(); i != stack->End(); i++)
			table->GetVariable(*i)->Print();
		printf("<Variables count = %d>\n", table->GetCount());
		
		(*current)->Evaluate(stack, table, this, &current);
	}
}

void Interpreter::buy(int count)
{
	if (count != 2) throw InvalidParams();
		
	VarsTable::Variable *price = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_int);
	VarsTable::Variable *number = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_int);
	
	bot->buy(*(int*)number->GetValue(), *(int*)price->GetValue());
}

void Interpreter::sell(int count)
{
	if (count != 2) throw InvalidParams();
		
	VarsTable::Variable *price = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_int);
	VarsTable::Variable *number = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_int);
	
	bot->sell(*(int*)number->GetValue(), *(int*)price->GetValue());
}

void Interpreter::prod(int count)
{
	if (count != 1) throw InvalidParams();
	
	VarsTable::Variable *number = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_int);	
	
	bot->prod(*(int*)number->GetValue());
}

void Interpreter::say(int count)
{
	if (count != 1) throw InvalidParams();
	
	VarsTable::Variable *string = table->Convert(table->GetVariable(stack->Pop()),
		VarsTable::Variable::v_string);	
	
	bot->say(*(String*)string->GetValue());
}
