#include "Den.h"
#include "../Interface/Actions.h"
#include <Utils/Margins.h>

namespace mechanics {

	void Wildling::checkAction() {
		if(!isAlive()) return;

		if(isMoving()) return;

		CheckAttack();

		if(currentGameTime>mNextAction && mOwner && isMoving() && !mAttackTarget && actionType==NONE) {
			mNextAction=currentGameTime+gge::utils::Random(MINUTE/2,MINUTE*2);//const

			int action=gge::utils::Random(0,2);
			switch(action) {
			case 1: {
				auto point=gge::utils::Random(mOwner->area());
				while(!Map::current().getPointInfoMap()(point).walkable)
					point=gge::utils::Random(mOwner->area());

				moveTo(point, mOwner->area()+gge::utils::Margins(assets::constants::general::pathingLimitMargin));
				break;
			}
			default:
				;
			}
		}
	}

	void Wildling::Killed(Attacker *attacker, BaseWeapon *weapon) {
		Attackable::Killed(attacker, weapon);

		PlayerGame::current().AddToGameLog("wildling killed.");
		animator.setAnimation(North, "Hurt", false);

		cancelMovement();

		mAttackTarget=NULL;
		actionTarget=NULL;

		if(mOwner) {
			mOwner->killWilding(this);
		}

		Map::current().getPointInfoMap()(location).walkable=true;
		Map::current().getPointInfoMap()(location).object=NULL;

		for(auto it=Map::current().destroyedEvents.begin();it!=Map::current().destroyedEvents.end();++it) {
			if(it->object==this && !it->isTriggered && interface::showEvent(*it)) {
				it->isTriggered=true;
			}
		}
	}

	bool Wildling::Dodge(Attacker *attacker, BaseWeapon *weapon) {
		float attackPrecision;

		if(attacker != NULL) {
			attackPrecision = attacker->attackPrecision();
		}
		else {
			//! constant
			attackPrecision = 0.5f;
		}

		if(mProvider.dodgeChance.calculate(level) * (1+mDodgeBonus/100) < attackPrecision) {
			if(mDodgeBonus<0)
				mDodgeBonus=0;
			else
				mDodgeBonus += assets::constants::combat::dodgeChanceModifier;

			return false;
		}
		
		if(mDodgeBonus>0)
			mDodgeBonus=0;
		else
			mDodgeBonus -= assets::constants::combat::dodgeChanceModifier;


		log("dodged");
		return true;
	}

	bool Wildling::Parry(Attacker *attacker, BaseWeapon *weapon) {
		float attackPrecision;

		if(currentGameTime - mLastAttackTime <= mWeaponCooldown) {
			return false;
		}

		if(attacker != NULL) {
			attackPrecision = attacker->attackPrecision();
		}
		else {
			//! constant, how can attacker be null
			attackPrecision = 0.5f;
		}

		if(mProvider.parryChance.calculate(level) * (1+mParryBonus/100) < attackPrecision) {

			if(mParryBonus<0) 
				mParryBonus=0;
			else
				mParryBonus += 20;
			return false;
		}

		//! constant, should work with base parry value
		if(mParryBonus>0)
			mParryBonus=0;
		else
			mParryBonus -= 20;

		mLastWeaponTime = currentGameTime - (mWeaponCooldown * 0.5f);

		log("parried");
		return true;
	}

	bool Wildling::Block(Attacker *attacker, BaseWeapon *weapon, float damage) {
		float attackPrecision;

		if(mBlockCooldown) {
			if(currentGameTime - mLastBlockTime <= mBlockCooldown) {
				return false;
			}
		}
		else {
			if(currentGameTime - mLastBlockTime <=mWeaponCooldown)
				return false;
		}

		if(attacker != NULL) {
			attackPrecision = attacker->attackPrecision();
		}
		else {
			//! constant
			attackPrecision = 0.5f;
		}

		if(mProvider.blockChance.calculate(level) * (1+mBlockBonus/100) < attackPrecision) {
			if(mBlockBonus<0)
				mBlockBonus=0;
			else
				mBlockBonus += assets::constants::combat::blockChanceModifier;

			return false;
		}

		if(mBlockBonus>0)
			mBlockBonus=0;
		else
			mBlockBonus -= assets::constants::combat::blockChanceModifier;

		if(mBlockCooldown) {
			mLastBlockTime = currentGameTime;
		}
		else {
			mLastWeaponTime = currentGameTime;
		}

		log("blocked");
		return true;
	}

	float Wildling::CalculateDamageReduction(Attacker *attacker, BaseWeapon *weapon, float damage) {
		float dmg = Attackable::CalculateDamageReduction(attacker, weapon, damage);
		dmg -= mProvider.armor.calculate(level);

		return dmg;
	}

	float Wildling::attackDamage() {

		return mDamage;

	}

	float Wildling::attackPrecision() {

		return mProvider.attack.calculate(level);

	}

	float Wildling::CalculateStagger(Attacker *attacker, BaseWeapon *weapon, float damage, float reducedDamage) {
		float stagger = Attackable::CalculateStagger(attacker, weapon, damage, reducedDamage);

		if(stagger > 0) {
			mLastAttackTime = currentGameTime + (mAttackCooldown * 2 * stagger / assets::constants::combat::staggerAtckCDDenominator);
		}

		return stagger;
	}

	void Wildling::CheckAttack() {
		std::vector<Human*> &humans = PlayerGame::current().getHumanList();
		std::vector<Building*> &buildings = PlayerGame::current().getBuildingList();

		if(mAttackTarget == NULL) {
			if( !actionTarget && actionType==NONE ) {
				int sightrange=sightRange();

				int mindistance=sightrange*sightrange;
				Object *selected=NULL;

				for(int x=location.x-sightrange;x<=location.x+sightrange;x++) {
					for(int y=location.y-sightrange;y<=location.y+sightrange;y++) {
						if(dynamic_cast<Human*>(Map::current().getPointInfoMap().Get(x,y).object) && dynamic_cast<Human*>(Map::current().getPointInfoMap().Get(x,y).object)->isAlive() && (x-location.x)*(x-location.x)+(y-location.y)*(y-location.y)<sightrange*sightrange) {
							int dist=gge::utils::Point(x,y).Distance(location);
							if(dist<mindistance) {
								mindistance=dist;
								selected=Map::current().getPointInfoMap()(x,y).object;
							}
						}
					}
				}

				if(selected) {
					attackTo(selected);
					return;
				}


				for(int x=location.x-sightrange;x<=location.x+sightrange;x++) {
					for(int y=location.y-sightrange;y<=location.y+sightrange;y++) {
						if(dynamic_cast<Building*>(Map::current().getPointInfoMap().Get(x,y).object) && dynamic_cast<Building*>(Map::current().getPointInfoMap().Get(x,y).object)->isAlive() && Map::current().getPointInfoMap().Get(x,y).object->getOwnerType()==OwnerType::player && (x-location.x)*(x-location.x)+(y-location.y)*(y-location.y)<sightrange*sightrange) {
							int dist=gge::utils::Point(x,y).Distance(location);
							if(dist<mindistance) {
								mindistance=dist;
								selected=Map::current().getPointInfoMap()(x,y).object;
							}
						}
					}
				}

				if(selected) {
					attackTo(selected);
					return;
				}
				
			}
			else if(!isMoving() && actionType==ATTACK && actionTarget) {
				actionTarget=NULL;
				actionType=NONE;
			}

			return;
		}

		auto human = find(humans.begin(), humans.end(), actionTarget);
		auto building = find(buildings.begin(), buildings.end(), actionTarget);

		if(human == humans.end() && building == buildings.end()) {
			actionTarget=NULL;
			actionType=NONE;
			mAttackTarget=NULL;
		}

		//dont say no to alternatives
		if(actionType==ATTACK && dynamic_cast<Building*>(actionTarget)) {
			int sightrange=sightRange();

			int mindistance=sightrange*sightrange;
			Object *selected=NULL;

			for(int x=location.x-sightrange;x<=location.x+sightrange;x++) {
				for(int y=location.y-sightrange;y<=location.y+sightrange;y++) {
					if(dynamic_cast<Human*>(Map::current().getPointInfoMap().Get(x,y).object) && dynamic_cast<Human*>(Map::current().getPointInfoMap().Get(x,y).object)->isAlive() && (x-location.x)*(x-location.x)+(y-location.y)*(y-location.y)<sightrange*sightrange) {
						int dist=gge::utils::Point(x,y).Distance(location);
						if(dist<mindistance) {
							mindistance=dist;
							selected=Map::current().getPointInfoMap()(x,y).object;
						}
					}
				}
			}

			if(selected) {
				attackTo(selected);
				return;
			}
		}

		if(actionTarget && actionTarget->currentLocation().Distance(location)>sightRange()) {
			actionTarget=NULL;
		}

		if(actionTarget==NULL) {
			mAttackTarget=NULL;
			mNextAction=currentGameTime+gge::utils::Random(MINUTE/2,MINUTE*2);//const
			setAnimation(currentDirection, "Stand");
			return;
		}

		//human = find(humans.begin(), humans.end(), mAttackTarget);
		//if(human == humans.end()) {
		//	mAttackTarget=NULL;
		//}

		if(!mAttackTarget || !mAttackTarget->isAlive()) {
			mAttackTarget = NULL;
			mNextAction=currentGameTime+gge::utils::Random(MINUTE/2,MINUTE*2);//const
			setAnimation(currentDirection, "Stand");
			actionType=NONE;
			actionTarget=NULL;
			return;
		}

		if(!inHitRange()) {
			mAttackTarget=NULL;
			checkPerformAction();
			return;
		}

		if(currentGameTime - mLastAttackTime <= mAttackCooldown) {
			return;
		}

		if(currentGameTime - mLastWeaponTime <= mWeaponCooldown) {
			return;
		}

		//if(!weapon->canBeUsed()) {
		//	return;
		//}

		//weapon->use();

		float damage = gge::utils::Random(0.f, attackDamage());
		float damagedone=mAttackTarget->Damage(this, NULL, damage);


		std::stringstream ss;
		ss << "attack";
		log(ss.str());

		auto targetlocation=actionTarget->currentLocation()-location;
		if(abs(targetlocation.x)>abs(targetlocation.y)) {
			if(targetlocation.x>0)
				currentDirection=East;
			else
				currentDirection=West;
		}
		else {
			if(targetlocation.y>0)
				currentDirection=South;
			else
				currentDirection=North;
		}

		setAnimation(currentDirection, "Attack", false, 1000/mWeaponCooldown);

		mLastAttackTime = currentGameTime;
		mLastWeaponTime = currentGameTime;
		mAttackCooldown=mWeaponCooldown*1.5f;
	}

	Wildling::Wildling(const assets::Wildling &given_provider, Animator &given_animator, int level) :
	mProvider(given_provider), mOwner(NULL), mAttackTarget(NULL),mLastWeaponTime(0), mLastAttackTime(0), mLastBlockTime(0),
		Object(given_animator, OwnerType::neutral), level(level), actionTarget(NULL),
		actionType(NONE), laststep(0), mDodgeBonus(0), mParryBonus(0), mBlockBonus(0) {
			currentDirection=(Direction)gge::utils:: Random(0,4);

			given_animator.setAnimation(currentDirection, "Stand");

			mNextAction=currentGameTime+gge::utils::Random(MINUTE/2,MINUTE*2);//!const

			HP(mProvider.HP.calculate(level));
			mDamage=mProvider.damage.calculate(level);
			mWeaponCooldown=mProvider.attackCooldown.calculate(level);
			mRange=mProvider.range.calculate(level);
			mOvershootRange=mProvider.overshootRange.calculate(level);

			mAttackCooldown=0;
			mLastAttackTime=0;

			mBlockCooldown=mProvider.blockCooldown.calculate(level);
			mLastBlockTime=0;
	}

}
