#include "consoleCommands.h"
#include "LargeInteger.h"
#include "TestWin.h"
#include "Muele.h"

gamestates::gamestates() : IC_Command(L"gamestates")
{
	setUsage(L"gamestates");
	addDescLine(L"Calculates number of gamestates for actual game settings.");
	addDescLine(L"");
}
gamestates::~gamestates()
{
}

void showBoard( int x, int y, unsigned char *board, IC_Console* pOutput , int time, int count) 
{

	WideString out = "";
	for ( int i = 0; i < x * y; i ++ ) {
		if ( *(board + i) == 1 ) out +="|x"; else
			if ( *(board + i) == 2 ) out+="|o"; else out+="| ";
		if ( (i+1) % y == 0 ) {
			out+="|";
			pOutput->logConsoleA(out);
			out="";
		}
	}
	out ="-------";
	out += time;
	out += ":";
	out +=count;
	pOutput->logConsoleA(out);
}



bool gamestates::invoke(const array<WideString>& args, IC_Console &pOutput)
{
	WideString inf;

	/*int boardSizeX = 3;
	int boardSizeY = 3;
	int boardSizeZ = 1;*/
	int boardSizeX = pMuele->boardSizeX;
	int boardSizeY = pMuele->boardSizeY;
	int boardSizeZ = pMuele->boardSizeZ;

	if ( boardSizeZ != 1 || boardSizeX != boardSizeY ||pMuele->winFieldCount != boardSizeX )
	{
		pOutput.logError(L"Only possible for quadratic boards with a height of 1 and connecting size of one board edge.");
		return true;
	}


	int boardFieldsCount = boardSizeX * boardSizeY * boardSizeZ;

	register char *pBoard  = new char[boardFieldsCount ];
	memset(pBoard, 0, boardFieldsCount );
	
	long boardStates = 1 ;
	long gameStates = 0;
	long winStates = 0;
	long preWinStates = 0;
	long impossibleStates = 0;
	int statesPlayer1 = 0, statesPlayer2 = 0;
	
	CLargeInteger liBoardStates(16);
	CLargeInteger liGameStates(0, 16);
	CLargeInteger liWinStates(0, 16);
	CLargeInteger liPreWinStates(0, 16);
	CLargeInteger liImpossibleStates(0, 16);
    
	CLargeInteger liPossibleStates(3, 16);
	liPossibleStates.pow(boardFieldsCount);
	inf = "possible board states: ";
	inf += liPossibleStates.toString();
	inf += " (3^";
	inf +=boardFieldsCount;
	inf += ")";
	pOutput.logConsoleA(inf);
	inf = "  current board state: 0";
	pOutput.logConsoleA(inf);
	inf = "     found gamestates: 0";
	pOutput.logConsoleA(inf);
	inf += "impossible gamestates: 0";
	pOutput.logConsoleA(inf);
	inf = "         elapsed time: 0";
	pOutput.logConsoleA(inf);
	

	pOutput.pVideoDriver->beginScene( true, true, irr::video::SColor(0) );
	pOutput.renderConsole();
	pOutput.pVideoDriver->endScene();

	int elapsedTime;
	
	u32 actualTime, updateTime;

	CTestWin testWinPlayer1( pBoard , NULL, boardSizeX, boardSizeY, boardSizeZ, PLAYER_1, pMuele->winFieldCount, 1);
	CTestWin testWinPlayer2( pBoard , NULL, boardSizeX, boardSizeY, boardSizeZ, PLAYER_2, pMuele->winFieldCount, 1);

	actualTime = updateTime = pOutput.pTimer->getRealTime();
	while ( pOutput.pTimer->getRealTime() - actualTime == 0 ) {}
	actualTime = updateTime = pOutput.pTimer->getRealTime();
	
	int playerCount[3] = {0, 0, 0};

	//char lastNoneZeroField = 0;
	register char field =  boardFieldsCount - 1; // start at field 1, field 0 not used(faster)
													// one more char for pBoard was allocated
	
	while( true ) 
	{

		(*(pBoard + field))++;

		if ( *(pBoard + field) == PLAYER_2 + 1 )
		{
			playerCount[PLAYER_2]--;
			*(pBoard + field) = -1;
			if ( field == 0  ) break;
			field--;
		} 
		else
		{
			playerCount[*(pBoard + field)]++;
			if ( *(pBoard + field) == 2 ) 
				playerCount[PLAYER_1]--;

			field++;
			if ( field == boardFieldsCount )
			{
				boardStates++;	
				field--;

				if ( ( playerCount[PLAYER_1] == playerCount[PLAYER_2] + 1 || playerCount[PLAYER_1] == playerCount[PLAYER_2] ) )
				{
					bool winPlayerOne = testWinPlayer1.testWin();
					bool winPlayerTwo = testWinPlayer2.testWin();

					if (( !winPlayerOne && !winPlayerTwo) )
					{
						preWinStates++;
						if ( playerCount[PLAYER_1] == playerCount[PLAYER_2] ) statesPlayer2++; else statesPlayer1++;
					}
					else
						if ( winPlayerOne && winPlayerTwo )
						{
							impossibleStates++;
						}
						else 
						{
							winStates++;
							if ( playerCount[PLAYER_1] == playerCount[PLAYER_2] ) statesPlayer2++; else statesPlayer1++;
						}
				}
					
			}
			else	
				if ( boardStates > 1000000 )
				{
					liBoardStates+=boardStates;
					boardStates=0;

					gameStates = preWinStates + winStates;
					liGameStates += gameStates;
					liPreWinStates += preWinStates;
					liWinStates += winStates;
					liImpossibleStates += impossibleStates;
					gameStates = preWinStates = winStates = impossibleStates = 0;

					if ( pOutput.pTimer->getRealTime() - updateTime > 1000 )
					{
						elapsedTime = pOutput.pTimer->getRealTime() - actualTime;

						pOutput.deleteLatestMessages(4);
						inf = "  current board state: ";
						inf += liBoardStates.toString();
						pOutput.logConsoleA(inf);
						inf = "     found gamestates: ";
						inf += liGameStates.toString();
						inf += "  prewin gamestates: ";
						inf += liPreWinStates.toString();
						inf += "  win gamestates: ";
						inf += liWinStates.toString();
						pOutput.logConsoleA(inf);
						inf = "impossible gamestates: ";
						inf += liImpossibleStates.toString();
						pOutput.logConsoleA(inf);
						inf = "         elapsed time: ";
						inf += (elapsedTime / 60000);
						inf += "m ";
						inf += (elapsedTime / 1000) % 60;
						inf += "s ";
						inf += elapsedTime  % 1000 ;
						inf += "ms";
						pOutput.logConsoleA(inf);

						pOutput.pVideoDriver->beginScene( true, true, irr::video::SColor(0) );
						pOutput.renderConsole();
						pOutput.pVideoDriver->endScene();

						updateTime = pOutput.pTimer->getRealTime();
					}
				}
		}
	} // while

	liBoardStates += boardStates;
	gameStates = preWinStates + winStates;
	liGameStates += gameStates;
	liPreWinStates += preWinStates;
	liWinStates += winStates;

	elapsedTime = pOutput.pTimer->getRealTime() - actualTime;
	
	delete[] pBoard;

	pOutput.deleteLatestMessages(4);
	inf = "  current board state: ";
	inf += liBoardStates.toString();
	pOutput.logConsoleA(inf);
	inf = "     found gamestates: ";
	inf += liGameStates.toString();
	inf += "  prewin gamestates: ";
	inf += liPreWinStates.toString();
	inf += "  win gamestates: ";
	inf += liWinStates.toString();
	pOutput.logConsoleA(inf);
	inf = "impossible gamestates: ";
	inf += liImpossibleStates.toString();
	pOutput.logConsoleA(inf);
	inf = "      states player 1: ";
	inf += statesPlayer1;
	inf += "  states player2: ";
	inf += statesPlayer2;
	pOutput.logConsoleA(inf);
	inf = "         elapsed time: ";
	inf += (elapsedTime / 60000);
	inf += "m ";
	inf += (elapsedTime / 1000) % 60;
	inf += "s ";
	inf += elapsedTime  % 1000 ;
	inf += "ms";
	pOutput.logConsoleA(inf);

	pOutput.logConsoleB(L"Calculation finished.");

	return true;
}
