// NeuralNetwork.cpp: implementation of the NeuralNetwork class.
//
//////////////////////////////////////////////////////////////////////

#include "Common/NeuralNet/NeuralNetwork.h"
#include "math.h"
#include "limits.h"
#include <stdlib.h>

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

NeuralNetwork::NeuralNetwork(int inCountInputLayer, int inCountHiddenLayer, double inAlpha, double inLambda, int inEnviromentSize, int inActionSize)
{
	int i;

	network = new TDlambda(inCountInputLayer, inCountHiddenLayer, inAlpha, inLambda);

	y = new double[inCountHiddenLayer];

	enviromentSize = inEnviromentSize;
	actionSize = inActionSize;
//	learning = false;

	network->Initialize();

	P = new double*[100];  //TODO

	for (i = 0; i < 100; i++)
		P[i] = new double[enviromentSize + actionSize];
}

NeuralNetwork::~NeuralNetwork()
{
	delete network;
}

void NeuralNetwork::CreateBinaryCode(int number, double *array, int maxBitCount)
{
	int j;

	for (j = maxBitCount - 1; j >= 0; j--)
	{
		if (number - pow((double)2, j) >= 0) 
		{
			array[j] = 1;
			number -= (int) pow((double)2, j);
		}
		else array[j] = 0;
	}
}


int NeuralNetwork::GetAction(double *enviroment, int *possibleActions, int actionsCount, int selection)
{
	int i, j, zPointer;
	int actionPointer = 0;

	double zMax;
	double *z;
	double *input;

	input = new double[enviromentSize + actionSize];

	z = new double[actionsCount];

	for(i = 0; i < actionsCount; i++)
	{
		//crate input for network - enviroment + action
		for (j = 0; j < enviromentSize; j++) input[j] = enviroment[j];
		for (j = enviromentSize; j < enviromentSize + actionSize; j++) input[j] = 0;
		input[enviromentSize + possibleActions[i]] = 1;

		//CreateBinaryCode(possibleActions[i], &input[enviromentSize], actionSize);

		//get network response for action possibleActions[i]
		z[i] = network->GetNetResponse(input, y);
	}

	switch (selection) {
	case ACTION_WINNER:
		{
			zPointer = -1;
			zMax = INT_MIN;

			for(i = 0; i < actionsCount; i++)
			{
				if (z[i] > zMax) 
				{
					zPointer = i;
					zMax = z[i];
				}
			}

			break;
		}
	case ACTION_RANDOM:
		{
			zPointer = (int)((double)rand()/(double)RAND_MAX * (actionsCount));
			while (zPointer > actionsCount - 1) zPointer = (int)((double)rand()/(double)RAND_MAX * (actionsCount));

			for (j = 0; j < enviromentSize; j++) input[j] = enviroment[j];

			for (j = enviromentSize; j < enviromentSize + actionSize; j++) input[j] = 0;
			input[enviromentSize + possibleActions[zPointer]] = 1;

			for (i = 0; i < enviromentSize + actionSize; i++) P[pPointer][i] = input[i];

			pPointer++;
			break;
		}
	case ACTION_ROULETTE:
		{
			double sum = 0;
			
			for(i = 0; i < actionsCount; i++) sum += z[i];

			double mul = (double)RAND_MAX / sum;

			int candidate = rand();

			sum = z[0] * mul;;

			for(i = 1; i < actionsCount; i++)
			{
				if (candidate < sum) break;
				sum += z[i] * mul;
			}

			zPointer = i - 1;
			
			for (j = 0; j < enviromentSize; j++) input[j] = enviroment[j];

			for (j = enviromentSize; j < enviromentSize + actionSize; j++) input[j] = 0;
			input[enviromentSize + possibleActions[zPointer]] = 1;

			//CreateBinaryCode(possibleActions[zPointer], &input[enviromentSize], actionSize);

			for (i = 0; i < enviromentSize + actionSize; i++) P[pPointer][i] = input[i];

			pPointer++;

			break;
		}
	}/**/
	
	delete [] z;
	delete [] input;
 
	if (zPointer == -1) return -1;
	return possibleActions[zPointer];
}


int NeuralNetwork::StartLearning()
{
	pPointer = 0;

	return 0;
}

int NeuralNetwork::EndLearning(double reward)
{
	network->GradientConstruction(P, reward, pPointer);

	return 0;
}