#include "Bot.h"
#include <algorithm>
#include "constants.h"

USING_NS_CC;

#define DECISION_MELEE_DT	50.0f
#define DECISION_RANGE_DT	100.0f

Bot::Bot(void)
{
}

Bot::Bot(UnitTemplate* u, Vec2 position, unsigned team, Node* parent, std::vector<Player*>* targets):
	Unit(u,position,team,parent),_targets(targets), _target(NULL)
{
	_selectedAttack = -1;
	_decisiontime = 0;
}


Bot::~Bot(void)
{
}

void Bot::Decide(std::vector<PRect*>* rects)
{
	if (_inAction)
		return;

	switch (_template->_behavior)
	{
	case BEHAVIOR_MELEE:
		DecideMelee(rects);
		_decisiontime = DECISION_MELEE_DT;
		break;
	case BEHAVIOR_RANGE:
		DecideRange(rects);
		_decisiontime = DECISION_RANGE_DT;
		break;
	default:
		break;
	}
}

cocos2d::Vec2 Bot::getVectorForProperty(AttackProp* prop)
{
	Vec2 targetPos = _target->getRect()->center;
	Vec2 pos = targetPos - prop->range*prop->direction;

	if (prop->direction.x>0)
	{
		Vec2 pos1 = pos + 2*prop->range*prop->direction;

		if (pos1.distance(_sprite->getPosition())< pos.distance(_sprite->getPosition()))
			pos = pos1;
	}

	return pos;
}

bool Bot::sortByDistance(std::pair<AttackProp*, int> a, std::pair<AttackProp*, int> b)
{
	Vec2 vec_a = getVectorForProperty(a.first);
	Vec2 vec_b = getVectorForProperty(b.first);
	Vec2 targetPos = _target->getRect()->center;

	return (vec_a.distance(_sprite->getPosition()) <
		vec_b.distance(_sprite->getPosition()));
}

bool Bot::MoveIntoGameRect(std::vector<PRect*>* rects)
{
	if (_rect.center.x>g_gameSize.getMaxX())
	{
		addDirection(-_moveDirection + dir_left,rects);
		return true;
	}
	else if (_rect.center.x<g_gameSize.getMinX())
	{
		addDirection(- _moveDirection + dir_right,rects);
		return true;
	}

	return false;
}

void Bot::DecideMelee(std::vector<PRect*>* rects)
{
	// if outside the game rect
	if (MoveIntoGameRect(rects))
	{
		return;
	}

	if (_targets->size()==1)
	{
		_target = (*_targets)[0];
	}
	else if (_targets->size()==2)
	{
		Unit* u1 = (*_targets)[0];
		Unit* u2 = (*_targets)[1];

		Vec2 pos1 = u1->getRect()->center;
		Vec2 pos2 = u1->getRect()->center;

		if ((_rect.center - pos1).length() < (_rect.center - pos2).length())
		{
			_target = u1;
		}
		else
		{
			_target = u2;
		}
	}
	else
	{
		 return;
	}

	// chose attacks
	std::vector<std::pair<AttackProp*, int> > props;
	
	std::vector<int>* allProps = &(_template->_attacks);
	for (unsigned i=0;i<allProps->size();i++)
	{
		AttackProp* prop = &((g_properties[(*allProps)[i]]));
		if (prop->type==ATTACK_THROW || prop->type == ATTACK_NONE)
			continue;

		props.push_back(std::pair<AttackProp*,int>(prop,i));
	}

	if (props.empty())
		return;
	//if in range of attack?

	for (unsigned i=0;i<props.size();i++)
	{
		AttackProp* prop = props[i].first;
		if(prop->type==ATTACK_SPECIAL)
		{
			if (_health>_template->_critHealth)
				continue;

			if (_specialCount==0)
				continue;
		}

		if (_calldowns[props[i].second]>0)
			continue;
	
		Vec2 position = _sprite->getPosition() + prop->range*Vec2(prop->direction.x,prop->direction.y);
		PRect r(position,prop->attackSize);

		if(rectConflict(&r,_target->getRect()))
		{
			if(prop->direction.x>0)
			{
				_facingRight = true;
				_sprite->setScaleX(1);
			}
			Attack(props[i].second);
			return;
		}

		if (prop->direction.x>0)
		{
			position.x -= prop->range*2*prop->direction.x;
			PRect r(position,prop->attackSize);
			
			if(rectConflict(&r,_target->getRect()))
			{
				_facingRight = false;
				_sprite->setScaleX(-1);
				Attack(props[i].second);
				return;
			}
		}
	}

	//move closer othewise

	//Step 1. Choose sth clothes position of attack
	{
	using namespace std::placeholders;
	
	std::sort(props.begin(),props.end(),std::bind(&Bot::sortByDistance,this, _1, _2));
	}
	//Choose movement way
	Vec2 current = _sprite->getPosition();
		
	AttackProp* prop = props[0].first;
	Vec2 destination = getVectorForProperty(prop);
		
		
	Vec2 direction = Vec2(sign(destination.x-current.x),sign(destination.y-current.y));

	addDirection(-_moveDirection+direction,rects);
}

void Bot::DecideRange(std::vector<PRect*>* rects)
{
	// if health <= critical than change behavior to melee
	if (_health <= _template->_critHealth)
	{
		DecideMelee(rects);
		return;
	}

	if (MoveIntoGameRect(rects))
	{
		return;
	}

	if (_targets->size()==1)
	{
		_target = (*_targets)[0];
	}
	else if (_targets->size()==2)
	{
		Unit* u1 = (*_targets)[0];
		Unit* u2 = (*_targets)[1];

		Vec2 pos1 = u1->getRect()->center;
		Vec2 pos2 = u1->getRect()->center;

		if ((_rect.center - pos1).length() > (_rect.center - pos2).length())
		{
			_target = u1;
		}
		else
		{
			_target = u2;
		}
	}
	else
	{
		 return;
	}	

	std::vector<std::pair<AttackProp*, int> > props;
	
	std::vector<int>* allProps = &(_template->_attacks);
	for (unsigned i=0;i<allProps->size();i++)
	{
		AttackProp* prop = &((g_properties[(*allProps)[i]]));
		if (prop->type!=ATTACK_THROW)
			continue;

		props.push_back(std::pair<AttackProp*,int>(prop,i));
	}

	if (props.empty())
		return;
	//if on line of fire

	for (unsigned i=0;i<props.size();i++)
	{
		AttackProp* prop = props[i].first;
		if(prop->type==ATTACK_SPECIAL)
		{
			if (_health>_template->_critHealth)
				continue;

			if (_specialCount==0)
				continue;
		}

		if (_calldowns[props[i].second]>0)
			continue; 

		Vec2 delta = _target->getRect()->center - _rect.center; 

		if(abs(delta.y) < _target->getRect()->size.y + prop->attackSize.y)
		{
			if (delta.x >0)
			{
				_facingRight = true;
				_sprite->setScaleX(1);
				
			}
			else
			{
				_facingRight = false;
				_sprite->setScaleX(-1);
			}
			Attack(props[i].second);
			return;
		}
	}
	

	// move? if out of range
	Vec2 delta = _target->getRect()->center - _rect.center;
	Vec2 direction = Vec2(0,sign(delta.y));

	// if there is no one in vertical direction move there

	if (!checkDirectionConflict(rects,direction,_template->_speed))
	{
		addDirection(-_moveDirection + direction,rects);
		return;
	}

	// otherwise, try diagonal

	direction.x = sign(delta.x);
	if (!checkDirectionConflict(rects,direction,_template->_speed))
	{
		addDirection(-_moveDirection +direction,rects);
		return;
	}

	//else move forward
	direction.y = 0;
	addDirection(-_moveDirection + direction,rects);
}

void Bot::UpdateMove(std::vector<PRect*>* rects, bool direction)
{
	if (!direction && !_inAction)
	{
		if (_decisiontime<=0)
		{	
			Decide(rects);
		}

		return;
	}

	Unit::UpdateMove(rects,direction);
}

void Bot::UpdateCalldown(float dt)
{
	if (_decisiontime>0)
		_decisiontime-=dt; 

	Unit::UpdateCalldown(dt);
}