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

#include "Player/World/SimPlayer.h"
#include <math.h>
#include <time.h>
#include "Common/Support/Log.h"
#include "Player/World/World.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////


#define max(x,y) ( ((x) > (y)) ? (x) : (y) )
#define min(x,y) ( ((x) > (y)) ? (y) : (x) )


SimPlayer::SimPlayer(World* w): Player(w)
{
	Reset();
}

SimPlayer::SimPlayer(const SimPlayer& other) : Player(other)
{
	stamina = other.stamina;
	effort = other.effort;
	recovery = other.recovery;
}

SimPlayer& SimPlayer::operator=(const SimPlayer& other)
{
	if(&other == this) return *this;

	Player::operator=(other);

	effort = other.effort;
	stamina = other.stamina;
	recovery = other.recovery;
	return *this;
}

SimPlayer::~SimPlayer()
{

}

int SimPlayer::PredictStepsToTarget()
{
	float dash;
	float dStamina;

	//log.Create(world->me.GetUniform());
	if(preparedCommands.activeTarget)
	{
		SimPlayer tmpPlayer(*this);
		//tmpPlayer.Reset();
//		tmpPlayer.preparedCommands.Debug();
		Vector diff = tmpPlayer.preparedCommands.targetPos - tmpPlayer.pos;
		int nSteps = 0;
		
		Vector v = (tmpPlayer.preparedCommands.targetPos - tmpPlayer.pos);
		Angle ta = tmpPlayer.GetAbsDir();-v.GetAngle();
		if (ta.GetDeg()>10){
			nSteps++;
		}
		tmpPlayer.SetAbsDir(v.GetAngle());
		world->log->printf("\nPredict %d", GetUniform());
		while (diff.GetLength() > tmpPlayer.preparedCommands.targetPrecision){
			dash = tmpPlayer.RecommDashPower(true);
			tmpPlayer.Step();
			// tmpPlayer.pos += tmpPlayer.vel;
			// tmpPlayer.vel *= type->player_decay;
			dStamina = (dash>=0) ? dash : -2*dash;
			tmpPlayer.stamina = tmpPlayer.stamina - dStamina;
			world->log->printf("\n\tk:%d->%f", nSteps, tmpPlayer.stamina);
			
			tmpPlayer.PredictNextVel(dash);
			tmpPlayer.PredictStamina();
						
			diff = tmpPlayer.preparedCommands.targetPos - tmpPlayer.pos;
			nSteps++;
			if (nSteps>100){
				printf("\nPredictStepsToTarget::break");
				nSteps = 5000;
				break;
			}
		}		
		return nSteps;
	}
	return 5000;
}

float SimPlayer::RecommDashPower(bool fast) const
{
	const float power_low = min((world->params->server->stamina_inc_max+type->extra_stamina) * world->params->server->recover_min, world->params->server->maxpower);

	const float stamina_low = world->params->server->stamina_max * world->params->server->effort_dec_thr;
	if(GetStamina() <= stamina_low) return power_low; //pod 30%
	if(GetEffort() < type->effort_max) return power_low; //je unaveny

	const float stamina_mid = world->params->server->stamina_max * world->params->server->effort_inc_thr;
	const float slow_powermax = min(world->params->server->stamina_inc_max, world->params->server->maxpower);
	
	if(fast) //bezime rychlo
	{
		if(GetStamina() < (stamina_mid)) //pod 60%
		{
			return slow_powermax + (GetStamina() - stamina_low) * (world->params->server->maxpower - slow_powermax)/(stamina_mid - stamina_low);
		}
		else //nad 60%
		{
			return world->params->server->maxpower;
		}
	}
	else //bezime pomaly
	{
		if(GetStamina() < stamina_mid) //pod 60%
		{
			return power_low;
		}
		else //nad 60%
		{
			
			if(GetStamina() < world->params->server->stamina_max) //pod 100%
			{
				return power_low + (GetStamina() - stamina_mid) * (slow_powermax - power_low)/(world->params->server->stamina_max - stamina_mid);
			}
			else //stamina je naplno
			{
				return slow_powermax;
			}
		}
	}
}

float SimPlayer::GetStamina() const
{
	return stamina;
}

float SimPlayer::GetEffort() const
{
	return effort;
}

void SimPlayer::PredictNextVel(float &DashPower)  
{
	if(DashPower > world->params->server->maxpower)
	{
		DashPower = world->params->server->maxpower;
	}
	else if(DashPower < world->params->server->minpower)
	{
		DashPower = world->params->server->minpower;
	}

	float Ep = DashPower * type->dash_power_rate * GetEffort();
	if(Ep > world->params->server->player_accel_max)
	{
		Ep = world->params->server->player_accel_max;
	}
	else if(Ep < -world->params->server->player_accel_max)
	{
		Ep = -world->params->server->player_accel_max;
	}

	float uhol = GetAbsDir().GetRad();
	RectVector nextVel(vel.GetX() + Ep * cosf(uhol), vel.GetY() + Ep * sinf(uhol));

	if(nextVel.GetLength() > type->player_speed_max)
	{
		nextVel *= (type->player_speed_max / nextVel.GetLength());
	}
	vel = nextVel;	
}

void SimPlayer::PredictStamina()
{
	if (stamina <= world->params->server->recover_dec_thr * world->params->server->stamina_max)
	{
		if (recovery > world->params->server->recover_min)
		{
			recovery = recovery - world->params->server->recover_dec;
		}
	}
		
	if (stamina <= world->params->server->effort_dec_thr*world->params->server->stamina_max)
	{
		if (effort > type->effort_min)
		{
			effort = effort - world->params->server->effort_dec;
		}
		effort = max(effort, type->effort_min);
	}

	if (stamina >= world->params->server->effort_inc_thr * world->params->server->stamina_max)
	{
		if (effort < type->effort_max)
		{
			effort = effort + world->params->server->effort_inc;
			effort = min(effort, type->effort_max);
		}
	}
	
	stamina = stamina + recovery * world->params->server->stamina_inc_max;
	stamina = min(stamina, world->params->server->stamina_max);
}

int SimPlayer::PredictStepsToBall(BALLARRAY &balls)
{
	int id=-1; 
	float fStep = 7000.0;
	int nStep;
    int i;
	nStepsToReachBall = 6000;
	if (!goalie)
		DBGN("\nStamina j%3.2f", stamina);
	for (i=0; i<balls.size(); i++){

		fStep = ((balls[i].pos - pos).GetLength())/type->player_speed_max;
		if (fStep-1.0<double(i)){
			break;
		}
	}
	
	SimPlayer p(*this);
	for (int j=i; j<balls.size(); j++){
		p.preparedCommands.SetTarget(balls[j]. pos);
		nStep = p.PredictStepsToTarget();
		if (nStep<=j){
			nStepsToReachBall = nStep;
			posToReachBall = balls[i].pos;
			return nStep;
		}
	}
	nStepsToReachBall = 6000;
	return 6000;
}

void SimPlayer::Reset()
{
    if (world->params->server) {
        //XXX at the beginning the value of
        //world->params->server is ZERO!!!!
	    stamina = world->params->server->stamina_max;
	    effort  = world->params->server->effort_init;
    }
	recovery = 1.0;
}

void SimPlayer::PredictNextPos(float dash)
{
	PredictNextVel(dash);
	Step();
}

bool SimPlayer::IsInMyKickArea(const Point &p)
{
	Circle c(world->me->pos, type->player_size+type->kickable_margin);
	return c.IsInside(p);
}
