#include "Symmetry.h"
#include "Muele.h"
#include "LargeInteger.h"

/********************************************************************/

CSymmetry::CSymmetry(int boardSizeX, int boardSizeY, int boardSizeZ) : boardSizeX(boardSizeX),
		boardSizeY(boardSizeY),	boardSizeZ(boardSizeZ), pBoardSymmetry(NULL)
{
	boardFieldCount = boardSizeX * boardSizeY * boardSizeZ;
	boardFieldCountPlain = boardSizeX * boardSizeY;

	pBoardSymmetry = new char[boardFieldCount];

	// There are 2 bits per field necessary to store the board as number.
	// A 9 Field board needs 2*9=18 bits are 3 bytes.
	int valueSize = (boardFieldCount * 2) / 8 + 1;

	// there are 8 possible symmetries, create one number for each
	for ( int i = 0; i < 8; i++ )
		symmetryValue[i] = new CLargeInteger(valueSize);
}

/********************************************************************/

CSymmetry::~CSymmetry(void)
{
	delete[] pBoardSymmetry;

	for ( int i = 0; i < 8; i++ )
		delete symmetryValue[i];
}

/********************************************************************/
/* Returns a pointer to a symmetric board, leaves the original board*/
/* untouched.														*/

char* CSymmetry::getSymmetry(char *pGameBoard)
{

//	return pGameBoard; // disable symmetries

	pBoard = pGameBoard;

	if ( boardSizeX == boardSizeY )
	{ 
		// 8 symmetries possible
		for ( int i = 0; i < 8; i++ )
			symmetryValue[i]->setZero();

		// calculate the numbers for all symmetries directly without creating the boards really
		// the calculations are taken from the 8 symmetrie methods
		for ( int z = 0; z < boardSizeZ; z++ )
			for ( int y = 0; y <  boardSizeY; y++ )
				for ( int x = 0; x < boardSizeX; x++ )
				{
					symmetryValue[0]->shiftLeftMax16(2);
					*(symmetryValue[0]) += FIELD_VALUE(pBoard, x, y, z);
					symmetryValue[1]->shiftLeftMax16(2);
					*(symmetryValue[1]) += FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, y, z);
					symmetryValue[2]->shiftLeftMax16(2);
					*(symmetryValue[2]) += FIELD_VALUE(pBoard, x, ( boardSizeY - 1 ) - y, z);
					symmetryValue[3]->shiftLeftMax16(2);
					*(symmetryValue[3]) += FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, ( boardSizeY - 1 ) - y, z);
					symmetryValue[4]->shiftLeftMax16(2);
					*(symmetryValue[4]) += FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - y, x, z);
					symmetryValue[5]->shiftLeftMax16(2);
					*(symmetryValue[5]) += FIELD_VALUE(pBoard, (boardSizeY - 1) - y	 , ( boardSizeX - 1 ) - x, z);
					symmetryValue[6]->shiftLeftMax16(2);
					*(symmetryValue[6]) += FIELD_VALUE(pBoard, y, ( boardSizeY - 1 ) - x, z);
					symmetryValue[7]->shiftLeftMax16(2);
					*(symmetryValue[7]) += FIELD_VALUE(pBoard, y, x, z);
				}

			int chooseBoard = 0;

			for ( int i = 1; i < 8; i++ )
				if ( (*symmetryValue[i]) < (*symmetryValue[chooseBoard]) ) chooseBoard = i;
		   
			// create the unique mapped symmetric board
			switch ( chooseBoard )
			{
			case 0: symmetryOriginal(); break;
			case 1: symmetryMirrorVertical(); break;
			case 2: symmetryMirrorHorizontal(); break;
			case 3: symmetryMirrorVerticalHorizontal(); break;
			case 4: symmetryRotateRight(); break;
			case 5: symmetryRotateRightMirrorVertical(); break;
			case 6: symmetryRotateLeft(); break;
			case 7: symmetryRotateLeftMirrorVertical(); break;
			}

		return pBoardSymmetry;
	}
	else
	{
		// only mirror symmetries possible
		for ( int i = 0; i < 4; i++ )
			symmetryValue[i]->setZero();

		// calculate the numbers for all symmetries directly without creating the boards really
		// the calculations are taken from the mirror symmetrie methods
		for ( int z = 0; z < boardSizeZ; z++ )
			for ( int y = 0; y <  boardSizeY; y++ )
				for ( int x = 0; x < boardSizeX; x++ )
				{
					symmetryValue[0]->shiftLeftMax16(2);
					*(symmetryValue[0]) += FIELD_VALUE(pBoard, x, y, z);
					symmetryValue[1]->shiftLeftMax16(2);
					*(symmetryValue[1]) += FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, y, z);
					symmetryValue[2]->shiftLeftMax16(2);
					*(symmetryValue[2]) += FIELD_VALUE(pBoard, x, ( boardSizeY - 1 ) - y, z);
					symmetryValue[3]->shiftLeftMax16(2);
					*(symmetryValue[3]) += FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, ( boardSizeY - 1 ) - y, z);
				}

			int chooseBoard = 0;

			for ( int i = 1; i < 4; i++ )
				if ( (*symmetryValue[i]) < (*symmetryValue[chooseBoard]) ) chooseBoard = i;
		   
			// create the unique mapped symmetric board
			switch ( chooseBoard )
			{
			case 0: symmetryOriginal(); break;
			case 1: symmetryMirrorVertical(); break;
			case 2: symmetryMirrorHorizontal(); break;
			case 3: symmetryMirrorVerticalHorizontal(); break;
			}

		return pBoardSymmetry;
	}
}


/**************************************************/

void CSymmetry::symmetryOriginal()
{
	memcpy( pBoardSymmetry, pBoard, boardFieldCount );
}

/**************************************************/

void CSymmetry::symmetryMirrorVertical()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, y, z);
}

/**************************************************/

void CSymmetry::symmetryMirrorHorizontal()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, x, ( boardSizeY - 1 ) - y, z);
}

/**************************************************/

void CSymmetry::symmetryMirrorVerticalHorizontal()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - x, ( boardSizeY - 1 ) - y, z);
}

/**************************************************/

void CSymmetry::symmetryRotateRight()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, ( boardSizeX - 1 ) - y, x, z);
}

/**************************************************/

void CSymmetry::symmetryRotateRightMirrorVertical()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, (boardSizeY - 1) - y, ( boardSizeX - 1 ) - x, z);
}

/**************************************************/

void CSymmetry::symmetryRotateLeft()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, y, ( boardSizeY - 1 ) - x, z);
}

/**************************************************/

void CSymmetry::symmetryRotateLeftMirrorVertical()
{
	for ( int z = 0; z < boardSizeZ; z++ )
		for ( int y = 0; y <  boardSizeY; y++ )
			for ( int x = 0; x < boardSizeX; x++ )
				FIELD_VALUE(pBoardSymmetry, x, y, z) = FIELD_VALUE(pBoard, y, x, z);
}

