#include "Unit.h"
#include "constants.h"
#include <cmath>
#include "Bullet.h"

using namespace cocos2d;

#define ACTION_ATTACK	1
#define ACTION_MOVE		2

#define ACTION_PUSHBACK	3
#define ACTION_STUN		4
#define ACTION_DEATH	5
#define ACTION_SLIDE	6
#define ACTION_MOVEANIM	7

#define PUSH_TIME			DT*30

#define ANIMTIME_DEATH		0.3f
#define ANIMTIME_MOVEMENT	0.3f
#define ANIMTIME_STUN		0.1f

#define PUSH_SPEED		30.0f

#define ANIMSIZE_MOVEMENT	2+1	
#define ANIMSIZE_ATTACK		4+1
#define ANIMSIZE_STUN		2+1
#define ANIMSIZE_DEATH		4+1


char str[100] = {0};
////////////////////////////////////////////

UnitTemplate::UnitTemplate()
{
}

UnitTemplate::~UnitTemplate()
{
}

UnitTemplate::UnitTemplate(unsigned id,
		int speed,
		int score,
		int health,
		int critHealth,
		int behavior,
		cocos2d::Vec2 size,
		std::vector<int>* attacks)
{
	_id = id;
	_speed = speed;
	_score = score;
	_health = health;
	_critHealth = critHealth;
	_behavior = behavior;
	_size = size;
	
	if (attacks)
		_attacks = std::vector<int>(*attacks);
	else
		_attacks.resize(0);
}


////////////////////////////////////////////////////////////////////////

Unit::Unit(void)
{
}

Unit::Unit(UnitTemplate* u, Vec2 position, unsigned team, Node* parent):
	_template(u)
{	
	
	sprintf(str, "units/%d.png", u->_id);
	_batch = SpriteBatchNode::create(str);

	SpriteFrameCache* cache = SpriteFrameCache::getInstance();
	sprintf(str, "units/%d.plist", u->_id);
	cache->addSpriteFramesWithFile(str);

	sprintf(str, "%d_S.png", u->_id);
	_sprite = Sprite::createWithSpriteFrameName(str);
	_batch->addChild(_sprite);
	_sprite->setPosition(position);
	_batch->setPosition(Vec2(0,0));
	parent->addChild(_batch);

	sprintf(str, "other/Stun.png");
	_stunBatch = SpriteBatchNode::create(str);
	_stunBatch->setPosition(Vec2(0,0));
	parent->addChild(_stunBatch);
	
	sprintf(str, "other/Stun.plist");
	cache->addSpriteFramesWithFile(str);

	_stunSprite = Sprite::createWithSpriteFrameName("STUN_1.png");
	_stunSprite->setOpacity(0);
	_stunBatch->addChild(_stunSprite);

	_rect = PRect(position,u->_size);
	_team = team;
	_template = u; 
	_inAction = false;
	_facingRight = true;
	_isMoving = false;
	//_cantMove = false;
	_dead = false;
	_pushDirecton = Vec2(0,0);

	_health=u->_health;
	
	for (unsigned i = 0;i<u->_attacks.size();i++)
	{
		if (g_properties[u->_attacks[i]].type == ATTACK_SPECIAL)
		{
			_specialCount = g_properties[u->_attacks[i]].count;
			_specialCountMax = _specialCount;
			break;
		}
	}

	_calldowns.resize(u->_attacks.size(),0);

	_attackIndex=1;
}

Unit::~Unit(void)
{
	_batch->getParent()->removeChild(_batch);
	//_batch->removeAllChildrenWithCleanup(false);
	_stunBatch->getParent()->removeChild(_stunBatch);
	//_stunBatch->removeAllChildrenWithCleanup(false);
	_template = NULL;
}

void Unit::StunFinished()
{
	_inAction= false;
	
}

void Unit::Attack(unsigned index)
{
	if (_inAction)
		return;

	if(_isMoving)
	{
		_sprite->stopActionByTag(ACTION_MOVE);
		_sprite->stopActionByTag(ACTION_MOVEANIM);
	}

	_attackIndex = index;

	AttackProp* prop = &(g_properties[_template->_attacks[index]]);
	
	if (prop->type == ATTACK_SPECIAL)
	{
		if (_specialCount>0 && _calldowns[index]<=0)
		{
			_specialCount--;
			_calldowns[index]= prop->calldown;
		}
		else
		{
			return;
		}
	}
	else
	{
		if (_calldowns[index]>0)
			return;
		else
			_calldowns[index]= prop->calldown;
	}

	_inAction = true;

	SpriteFrameCache* cache = SpriteFrameCache::getInstance();
	cocos2d::Vector<cocos2d::SpriteFrame*> attack1;

	for(int i = 1; i < ANIMSIZE_ATTACK; i++) 
	{
		sprintf(str, "%d_A%d_%d.png", _template->_id, index,i);
		SpriteFrame* frame = cache->getSpriteFrameByName( str );
		attack1.pushBack(frame);
	}

	Sequence* seq = Sequence::createWithTwoActions(
		Repeat::create(Animate::create(Animation::createWithSpriteFrames(attack1, prop->animDT)),1),
		CallFunc::create( std::bind(&Unit::ActionStopped,this)));
	seq->setTag(ACTION_ATTACK);
	_sprite->runAction(seq);
}

void Unit::ActionStopped()
{
	AttackProp* prop = &(g_properties[_template->_attacks[_attackIndex]]);

	if (prop->selfDestruction)
	{
		_dead = true;
	}
	else
	{
		SpriteFrameCache* cache = SpriteFrameCache::getInstance();
		sprintf(str, "%d_S.png", _template->_id);
		SpriteFrame* frame = cache->getSpriteFrameByName( str );
		_sprite->setSpriteFrame(frame);
		_inAction = false;
	}
	
	int one=1;
	if(prop->direction.x>0 && !_facingRight)
	{
		one=-1;
	}
	
	Vec2 position = _sprite->getPosition() + prop->range*Vec2(one*prop->direction.x,prop->direction.y);
	_attackedRect = ARect(position,prop,_team,one*prop->direction);
	if (_isMoving)
	{
		StartMoveAnim();
		Move();
	}
}

cocos2d::SpriteBatchNode* Unit::GetBatch()
{
	return _batch;
}

Vec2 Unit::countDelta(Vec2 direction)
{
	Vec2 delta(0,0);
	if (direction.x >0)
		delta.x = g_gameSize.getMaxX() - _sprite->getPosition().x;
	else if (direction.x <0)
		delta.x = g_gameSize.getMinX() - _sprite->getPosition().x;

	if (direction.y >0)
		delta.y = g_gameSize.getMaxY() - _sprite->getPosition().y;
	else if (direction.y <0)
		delta.y = g_gameSize.getMinY() - _sprite->getPosition().y;

	return delta;
}

void Unit::Move()
{
	_sprite->stopActionByTag(ACTION_MOVE);

	Vec2 delta = countDelta(_moveDirection);
	 
	float length= ccpLength(delta);
	float time = length/_template->_speed;

	Action* act = MoveBy::create(time,delta);
	act->setTag(ACTION_MOVE);

	_sprite->runAction(act);
}

void Unit::stopMoving()
{
	_sprite->stopActionByTag(ACTION_MOVE);
	_sprite->stopActionByTag(ACTION_MOVEANIM);

	SpriteFrameCache* cache = SpriteFrameCache::getInstance();
	sprintf(str, "%d_S.png", _template->_id);
	SpriteFrame* frame = cache->getSpriteFrameByName( str );
	_sprite->setSpriteFrame(frame);
}

bool Unit::checkDirectionConflict(std::vector<PRect*>* rects, cocos2d::Vec2 direction, float speed)
{
	Vec2 newPos = ccpAdd(_sprite->getPosition(),direction*DT*speed);
	PRect newRect(newPos,_template->_size);

	for (unsigned i=0;i<rects->size();i++)
	{
		PRect* rect = (*rects)[i];
		if (rect != &_rect)
			if(rectConflict(&newRect,rect))
			{
				return true;
				break;
			}
	}

	return false;
}

void Unit::UpdateMove(std::vector<PRect*>* rects, bool direction)
{
	if (_inAction)
		return;

	/*if (_pushDirecton.x !=0 || _pushDirecton.y!=0)
	{
		if (checkDirectionConflict(rects,_pushDirecton,PUSH_SPEED))
		{
			_sprite->stopActionByTag(ACTION_PUSHBACK);
			_pushDirecton = Vec2(0,0);
		}
	}*/

	if (_slideCalldown<=0)
	{
		_sprite->stopActionByTag(ACTION_SLIDE);
		Vec2 delta(0,0);
		int count =0;
		for (unsigned i=0;i<rects->size();i++)
		{
			PRect* rect = (*rects)[i];
			if (rect != &_rect)
				if(rectConflict(&_rect,rect))
				{
					delta += (_rect.center - rect->center);
					count++;
				}
		}
		
		if (count>0)
		{
			Action* act = MoveBy::create(PUSH_TIME,delta/(2*count));
			act->setTag(ACTION_SLIDE);
			_sprite->runAction(act);

		}

		_slideCalldown = PUSH_TIME;
	}

	/*bool newCantMove= checkDirectionConflict(rects,_moveDirection,_template->_speed);

	if (!_cantMove && newCantMove)
		stopMoving();
	
	if (_cantMove && !newCantMove && !direction)
		Move();

	_cantMove = newCantMove;

	if (!_cantMove && direction)*/
	if (direction)
		Move();
}

void Unit::StartMoveAnim()
{
	SpriteFrameCache* cache = SpriteFrameCache::getInstance();
		cocos2d::Vector<cocos2d::SpriteFrame*> movement;

		for(int i = 1; i < ANIMSIZE_MOVEMENT; i++) 
		{
			sprintf(str, "%d_M_%d.png", _template->_id,i);
			SpriteFrame* frame = cache->getSpriteFrameByName( str );
			movement.pushBack(frame);
		}
		Action* act = RepeatForever::create(
			Animate::create(
				Animation::createWithSpriteFrames(
					movement, 
					ANIMTIME_MOVEMENT)));
		act->setTag(ACTION_MOVEANIM);
		_sprite->runAction(act);
}

void Unit::addDirection(cocos2d::Vec2 direction, std::vector<PRect*>* rects)
{
	_moveDirection.add(direction);
	bool moved =_isMoving;

	if (_moveDirection.x==0 && _moveDirection.y==0)
	{
		_isMoving = false;
		if (!_inAction)
			stopMoving();
		return;
	}
	else
	{
		_isMoving =true; 

		if (!moved && !_inAction)
		{
			StartMoveAnim();
		}
	}


	UpdateDirection();

	UpdateMove(rects, true);
}

void Unit::UpdateDirection()
{
	if ((_moveDirection.x<0 && _facingRight) ||(_moveDirection.x>0 && !_facingRight))
	{
		_facingRight = !_facingRight;

		_sprite->setScaleX(1);
	if (!_facingRight)
		_sprite->setScaleX(-1);
	}
}

void Unit::UpdateRect()
{
	_rect.center = _sprite->getPosition();
}

PRect* Unit::getRect()
{
	return &_rect;
}

void Unit::UpdateZOrder(unsigned z)
{
	_batch->setZOrder(z);
	_stunBatch->setZOrder(z+1);
}

void Unit::UpdateCalldown(float dt)
{
	for (unsigned i=0;i<_calldowns.size();i++)
	{
		if(_calldowns[i]>0)
			_calldowns[i]-=dt;
	}

	if (_slideCalldown>0)
		_slideCalldown-=dt;
}

void Unit::Attacked(ARect* attackedRect)
{
	if (!rectConflict(attackedRect,&_rect) || attackedRect->team==_team)
		return;

	AttackProp* prop = attackedRect->prop;
	_health-= prop->damage;

	if (prop->stun)
	{
		_sprite->stopActionByTag(ACTION_MOVE);
		_sprite->stopActionByTag(ACTION_ATTACK);
		_inAction = true;

		SpriteFrameCache* cache = SpriteFrameCache::getInstance();

		Vector<SpriteFrame*> stunAnim;
		for (unsigned i=1;i<ANIMSIZE_STUN;i++)
		{
			sprintf(str, "STUN_%d.png",i);
			SpriteFrame* frame = cache->getSpriteFrameByName( str );
			stunAnim.pushBack(frame);
		}

		Sequence* seq = Sequence::create(
			Repeat::create(
				Animate::create(
					Animation::createWithSpriteFrames(stunAnim, ANIMTIME_STUN)),5),
			FadeOut::create(ANIMTIME_DEATH),
			CallFunc::create( std::bind(&Unit::StunFinished,this)),NULL);

		
		Vec2 position = Vec2(_sprite->getPosition().x, 
			_sprite->getPosition().y + 
			_sprite->getSpriteFrame()->getOriginalSize().height/2);
		_stunSprite->setPosition(position);
		_stunSprite->setOpacity(255);

		_stunSprite->runAction(seq);
		
	}
	
	if (prop->pushBack)
	{
		_pushDirecton = attackedRect->direction*PUSH_SPEED;
		Vec2 delta = countDelta(attackedRect->direction);

		_pushDirecton = Vec2( sign(_pushDirecton.x)*std::min(abs(_pushDirecton.x),abs(delta.x)),sign(_pushDirecton.y)*std::min(abs(_pushDirecton.y),abs(delta.y)));

		Action* act = MoveBy::create(PUSH_TIME,_pushDirecton);
		act->setTag(ACTION_PUSHBACK);
		_stunSprite->runAction(act->clone());
		_sprite->runAction(act);
	}

}

void Unit::Die()
{
	_dead = true;
}

unsigned Unit::GetTeam()
{
	return _team;
}

ARect Unit::GetAttackRect()
{
	ARect r = _attackedRect;
	_attackedRect=ARect();
	return r;
}

bool Unit::isDead()
{
	return _dead;
}

void Unit::CheckItemPickUp(Item* item)
{
	if (_team == item->getTeam())
		return;

	if (!rectConflict(&_rect,item->getRect()))
		return;

	UseItem(item);
}

void Unit::UseItem(Item* item)
{
	switch(item->getType())
	{
	case ITEM_TYPE_AP:
		if (_specialCount<_specialCountMax)
		{
			_specialCount++;
			item->setUsed();
		}
		break;
	case ITEM_TYPE_HP:
		_health = _template->_health;
		item->setUsed();
		break;
	case ITEM_TYPE_BULLET:
		{
			Bullet* bullet = (Bullet*) item;
			Vec2 position = bullet->getRect()->center;
			ARect r(position,bullet->_property,bullet->getTeam(),bullet->_direction);
			r.size = item->getRect()->size;
			Attacked(&r);
			item->setUsed();
		}
		break;
	default:
		break;
	}
}

void Unit::CheckHealth()
{
	if (_health<=0)
	{
		_health = 0;
		if (_sprite->getActionByTag(ACTION_DEATH) != NULL || _dead)
			return;

		_inAction = true;
		_sprite->stopAllActions();
		_stunSprite->setVisible(false);

		SpriteFrameCache* cache = SpriteFrameCache::getInstance();
		cocos2d::Vector<cocos2d::SpriteFrame*> death;

		for(int i = 1; i < ANIMSIZE_DEATH; i++) 
		{
			sprintf(str, "%d_D_%d.png", _template->_id,i);
			SpriteFrame* frame = cache->getSpriteFrameByName( str );
			death.pushBack(frame);
		}

		Sequence* seq = Sequence::create(
			Repeat::create(
				Animate::create(
					Animation::createWithSpriteFrames(death, ANIMTIME_DEATH)),1),
			FadeOut::create(ANIMTIME_MOVEMENT),
			CallFunc::create( std::bind(&Unit::Die,this)),NULL);
		seq->setTag(ACTION_DEATH);
		_sprite->runAction(seq);
	}
}

/////////////////////////
bool compareUnitByZOrder(Unit* u1,Unit* u2)
{
	return (u1->_sprite->getPosition().y > 
		u2->_sprite->getPosition().y);
}

