#include "Player/PlayerSkills.h"
#include "Player/PlayerTactics.h"
#include "Common/Main.h"
#include "Common/Tasks.h"
#include "Common/Support/Debug.h"
#include <stdio.h>
#include <math.h>
#include <assert.h>
#include <string.h>
#include <limits.h>

#include "DataTypes.h"


#define POS_ERROR_DELTA 1




void PlayerSkills::GetMaximalPassDistance(ASPass* as)
{
	Vector BallDiff = world->ball->pos - world->me->pos;
	float BallDistance = BallDiff.GetLength();

	float dir_diff = (BallDiff.GetAngle() - world->me->GetAbsDir()).GetAbsDeg();
	if(dir_diff < 0.f) dir_diff = -dir_diff;

	const float ball_power_rate = world->params->server->kick_power_rate *
			(1 - 0.25f * (dir_diff/180) - 0.25f * (BallDistance - world->me->type->player_size - world->params->server->ball_size) / world->me->type->kickable_margin);

	float maxacc = world->params->server->maxpower * ball_power_rate;
	
	if(maxacc > world->params->server->ball_accel_max)
	{
		maxacc = world->params->server->ball_accel_max;
	}

	float maxd = maxacc;
	float ballvel = maxacc;

	while(ballvel > 1.0f)
	{
		ballvel *= world->params->server->ball_decay;
		maxd += ballvel;
	}


	TXTN("GetMaximalPassDistance : %f,   maxacc %f, dir_diff %f", maxd, maxacc, dir_diff);

	as->maxballspeed = maxacc;
	as->passlimit = maxd;
}






float PlayerTactics::ValuePlace(Point place)
{
	if(((place.GetX() < world->params->flags[LT].GetX() - POS_ERROR_DELTA) ||
		(place.GetY() < world->params->flags[LT].GetY() - POS_ERROR_DELTA) ||
		(place.GetX() > world->params->flags[RB].GetX() + POS_ERROR_DELTA) ||
		(place.GetY() > world->params->flags[RB].GetY() + POS_ERROR_DELTA)))
	{
		return 0;
	}

	float value = GetNearestEnemyDistance(place);
	return value;
}



bool PlayerTactics::GetPassAbleFriend(int player, bool needPass, bool preferAttacker, ASPass* as)
{

	int bestDir = -1, bestAttackDir = -1;
	Player* pl = (player == -1) ? world->me : &(world->friends[player]);

	float netRes = 0.f;

	int bestFriend = FCGetPassAbleFriend(player, needPass, preferAttacker, netRes, as);

	int pln = (bestFriend < 0) ? bestFriend : world->friends[bestFriend].GetUniform();
	TXTN("%i: FCGetPassAbleFriend bestFriend : %d, age %d", world->state->GetSimTime(), pln,
		(bestFriend < 0) ? -1 : world->friends[bestFriend].GetAge());

	as->prob = netRes;

	if(bestFriend != -2)
	{
		Player* me = (bestFriend == -1) ? world->me : &(world->friends[bestFriend]);

		float bestDirValue = ValuePlace(me->pos);
		float bestAttackDirValue = bestDirValue;

		TXTN("%i: GetPassAbleFriend Value of place -1 : %f", world->state->GetSimTime(), bestDirValue);

		for(int i=0;i<8;i++)
		{
			Point place = me->pos + PolarVector(CATCH_DIST, DegAngle(45.0f*i));
			
			if((place - pl->pos).GetLength() < world->variables->GetPassSurroundingsMinWidth()) continue;

			float value = ValuePlace(place);

			TXTN("%i: GetPassAbleFriend Value of place %d pos %f, %f: %f", world->state->GetSimTime(), i, 
				place.GetX(), place.GetY(), value);

			if(value > bestDirValue) 
			{
				bestDir = i;
				bestDirValue = value;
			}

			if((i==0 || i==1 || i==7) && (value > bestAttackDirValue))
			{
				bestAttackDir = i;		
				bestAttackDirValue = value;
			}
		}
			
		if(bestDirValue < 4.0f) bestDir = -1;
		if(preferAttacker && (bestAttackDirValue >= 10.0f))
		{
			bestDir = bestAttackDir;
		}

		as->passFrom = player;
		as->passTo = bestFriend;
		as->inDir = bestDir;
		
		if(bestDir != -1)
		{
			as->pass_distance = CATCH_DIST;
			as->catch_point = me->pos + PolarVector(CATCH_DIST, DegAngle(45.0f*bestDir));
		}
		else
		{
			as->pass_distance = 0;
			as->catch_point = me->pos;
		}

		TXTN("%i: GetPassAbleFriend inDir : %d", world->state->GetSimTime(), bestDir);
		TXTN("%i: GetPassAbleFriend catch_point %f, %f", world->state->GetSimTime(), 
			as->catch_point.GetX(), as->catch_point.GetY());
		
		return true;
	}

	return false;
}

float PlayerTactics::EstimatePassPosForEnemy(int enemy, Ball* ball, int simCycles)
{
	Ball tmpBall(*ball);

	int enemycatch = simCycles;

	float distance = 0.f, balldist, speed = world->params->server->player_speed_max;
	float karea = world->params->server->player_size + world->params->server->ball_size + world->params->server->kickable_margin;

	for(int i=0; i <= simCycles; i++)
	{
		balldist = (tmpBall.pos - world->enemies[enemy].pos).GetLength();

		if((balldist - distance) < karea) 
		{
			if(i==0) continue;
			enemycatch = i;
			break;
		}

		distance += speed;
		tmpBall.Step();
	}

	TXTN("EstimatePassPosForEnemy %d    catch = %d", world->enemies[enemy].GetUniform(), enemycatch);
	return ((float)enemycatch)/simCycles;
}



void PlayerSkills::PassBallToPlayer(ASPass* as)
{
	if(as->action == PASSTOPLAYER)
	{
		TXTN("%i: PassBallToPlayer catch_point %f, %f", world->state->GetSimTime(), 
				as->catch_point.GetX(), as->catch_point.GetY());

		DontRunAfterBall = 3;
		PassBallTo(as->catch_point);
	}

	if(as->action == PASSSPRACUJLOPTU)
	{
		TXTN("%i: PassBallToPlayer spracuj loptu");
		SpracujLoptu();
	}
}

bool CanExitOffside(int player, Point newballpos, int numcycles)
{
	return true;
}

bool PlayerTactics::ShallPass(int player)
{
	//return true;

	int uniform = (player == -1) ? world->me->GetUniform() : world->friends[player].GetUniform();
	Player* pl = (player == -1) ? world->me : &(world->friends[player]);

	int playerdir = PlayerDir(player);
	if(playerdir == -1) playerdir = 0;

	playerdir = (playerdir < 4) ? playerdir + 4 : playerdir - 4; 

	if(uniform != 0)
	{
		FormationRole role = m_cFormation.GetPlayerRole(uniform);
		Point pos = m_cFormation.GetPlayerPosition(uniform, world->ball->pos);

		TXTN("SHALLPASS : miesto vo formacii %f, %f", pos.GetX(), pos.GetY());
		Vector dist = pos - world->me->pos;

		if(pl->pos.GetX() >= 30.0f) return true; 

		/*if(dist.GetLength() > 10.0)
		{
			TXTN("SHALLPASS : hrac daleko od miesta vo formacii = %f", dist.GetLength());
			return true;
		}*/

		float staminamid = (world->params->server->stamina_max * world->params->server->effort_dec_thr + 
					world->params->server->stamina_max * world->params->server->effort_inc_thr) / 2;

		if(role == PR_Defensman) return true;

		if(role == PR_Midfielder)
		{
			int num = NumPlayersInSector(true, false, pl->pos, 0, 360.0, 0.0f, 4.0f);

			TXTN("SHALLPASS : pocet nepriatelov 360 stupnov dlzka 4 = %d", num);
			if(num != 0) return true;

			num = NumPlayersInSector(false, true, pl->pos, 0, 90.0, 1.0f, 10.0f);
			int nume = NumPlayersInSector(true, false, pl->pos, 0, 90.0, 1.0f, 10.0f);

			if((num == 1) && (nume == 0))
			{
				TXTN("SHALLPASS : predomnou je nejaky spoluhrac mal by som mu prihrat");
				return true;
			}

			if(player == -1)
			{
				if(world->me->GetStamina() < staminamid) return true;
			}

		}

		if(role == PR_Forward)
		{
			int num = NumPlayersInSector(true, false, pl->pos, playerdir, 220, 0.0f, 2.0f);

			TXTN("SHALLPASS : pocet nepriatelov v smere %d 220 stupnov dlzka 3 = %d", playerdir, num);
			if(num != 0) return true;

			num = NumPlayersInSector(false, true, pl->pos, 0, 90.0, 1.0f, 10.0f);
			int nume = NumPlayersInSector(true, false, pl->pos, 0, 90.0, 1.0f, 10.0f);

			if((num == 1) && (nume == 0))
			{
				TXTN("SHALLPASS : predomnou je nejaky spoluhrac mal by som mu prihrat");
				return true;
			}

			if(player == -1)
			{
				if(world->me->GetStamina() < staminamid) return true;
			}
	
		}
	}

	return false;
}



ASPass* PlayerTactics::TryPass(int player)
{
	Player* pl = (player == -1) ? world->me : &(world->friends[player]);

	ASPass* as = new ASPass;
	as->type = PASS;

	as->passlimit = 30.0f;
	as->maxballspeed = world->params->server->ball_speed_max;

	if(player == -1) GetMaximalPassDistance(as);

	if(ShallPass(player))
	{
		TXTN("%i: ->player %d should pass", world->state->GetSimTime(), player);
		TXTN("TryPass : Vzdialenost najblizsieho protihraca %f", GetNearestEnemyDistance(pl->pos));

		if(GetNearestEnemyDistance(pl->pos) >= 3.0f)
		{
			if(GetPassAbleFriend(player, false, true, as))
			{
				if(as->prob > 0.8f)
				{
					as->payoff = (int)(10 * as->prob);
					as->action = PASSTOPLAYER;

					TXTN("TryPass : payoff je %d shall pass", as->payoff);
					return as;
				}
				else TXTN("TryPass : mala pravdepodobnost na prihranie");
			}
			else
			{
				if(as->wolimit && (as->probwolimit > 0.8f))
				{
					as->payoff = (int)(10 * as->probwolimit);
					TXTN("TryPass : mal by som prihrat ale mam hraca len mimo dosahu do ktoreho by som mal kopat, preto spracuvam loptu payoff %d",
						as->payoff);
					as->action = PASSSPRACUJLOPTU;
				}
			}
		}
		else
		{
			/*if((pl->pos.GetX() >= 30.0f) && GetPassAbleFriendInFormation(as->catch_point, as->prob))
			{
				as->payoff = (int)(10 * as->prob);
				as->action = PASSTOPLAYER;

				TXTN("TryPass : payoff je %d prihravanie do formacie", as->payoff);
				return as;
			}
			else*/ if(GetPassAbleFriend(player, true, true, as))
			{
				as->payoff = (int)(10 * as->prob);
				as->action = PASSTOPLAYER;

				TXTN("TryPass : payoff je %d shall pass needPass", as->payoff);
				return as;
			}
		}

	}
	else
	{
		if(GetPassAbleFriend(player, true, false, as))
		{
			TXTN("Pravdepodobnost nudzovej prihravky je %f hracovi %d", as->prob, as->passTo);

			if(as->prob > 0.7f)
			{
				as->payoff = 2;
				as->action = PASSTOPLAYER;
				return as;
			}
		}

	}

	as->payoff = -1;
	as->action = DONOTHING;
	return as;
}


