#pragma once

#include "../Assets/Wildling.h"
#include "Attacker.h"
#include "Attackable.h"
#include "Map.h"
#include "Mover.h"
#include "PlayerGame.h"

namespace mechanics {

	class Den;

	class Wildling : public Object, public Attacker, public Attackable, public Mover, public FloatingLogger {
		friend class WildlingSpawner;
	public:
		enum ActionType {
			NONE,
			ATTACK
		};

		Wildling(const assets::Wildling &given_provider, Animator &given_animator, int level);

		virtual void locateTo(const gge::utils::Point &given_location)
		{
			Object::locateTo(given_location);	
			mechanics::Map::current().getPointInfoMap()(location).walkable=false;
			mechanics::Map::current().getPointInfoMap()(location).object=this;
		}

		void attackTo(mechanics::Object *target) {
			if(dynamic_cast<mechanics::Attackable *>(target)) {
				actionTarget=target;
				actionType=ATTACK;
				checkPerformAction();
			}
		}

		virtual void setLocation(const gge::utils::Point &given_location) {
			if(given_location == location) {
				return;
			}

			Map::current().getPointInfoMap()(location).walkable = true;
			Map::current().getPointInfoMap()(location).object=NULL;
			//!if there is another object below the guy switch it to object

			Object::setLocation(given_location);

			Map::current().getPointInfoMap()(location).walkable = false;
			Map::current().getPointInfoMap()(location).object=this;
		}

		virtual void removeFromMap() {
			Map::current().getPointInfoMap()(location).walkable = true;
			Map::current().getPointInfoMap()(location).object=NULL;
			Object::removeFromMap();
		}

		virtual float movementSpeed() const {
			return mProvider.movementSpeed.calculate(level); //!constant
		}

		virtual bool Dodge(Attacker *attacker, BaseWeapon *weapon);
		virtual bool Parry(Attacker *attacker, BaseWeapon *weapon);
		virtual bool Block(Attacker *attacker, BaseWeapon *weapon, float damage);
		virtual float CalculateDamageReduction(Attacker *attacker, BaseWeapon *weapon, float damage);
		virtual float attackDamage();
		virtual float attackPrecision();
		virtual float CalculateStagger(Attacker *attacker, BaseWeapon *weapon, float damage, float reducedDamage);

		float sightRange() const {
			return isNight() ? assets::constants::general::wildlingNightSight : assets::constants::general::wildlingDaySight; //!constant
		}


		virtual void moveFinished() {
			checkPerformAction();
			if(actionType==NONE)
				mNextAction=currentGameTime+gge::utils::Random(MINUTE/assets::constants::general::wildlingWanderRangeDenominator,MINUTE*assets::constants::general::wildlingWanderRangeMultiplier);//const
		}
		
		virtual float maximumHP() const {
			return mProvider.HP.calculate(level);
		}

		virtual float XPReward() const {
			return mProvider.XPReward.calculate(level)/maximumHP();
		}

		virtual std::string title() const {
			return mProvider.ID;
		}

		virtual void setMoveDirection(Direction direction) {
			currentDirection=direction;
			animator.setDirection(direction, "Walk");
		}

		virtual void animSetMove(Direction direction, float speed) {
			currentDirection=direction;
			animator.setAnimation(direction, "Walk", true, speed);
		}

		virtual void animStopMove(Direction direction) {
			currentDirection=direction;
			animator.setAnimation(direction, "Stand");
		}

		virtual void Killed(Attacker *attacker, BaseWeapon *weapon);

		void checkAction();

		void CheckAttack();

		void moveStep() {
			checkPerformAction();
		}

		bool inHitRange() {
			if(mRange>0) {
				if(mOvershootRange/32.f > actionTarget->currentLocation().Distance(location) || mRange/32.f > actionTarget->currentLocation().Distance(location)) {
					return true;
				}
			}
			else if(
				Map::current().getPointInfoMap().Get(location.x, location.y-1).object==actionTarget ||
				Map::current().getPointInfoMap().Get(location.x, location.y+1).object==actionTarget ||
				Map::current().getPointInfoMap().Get(location.x-1, location.y).object==actionTarget ||
				Map::current().getPointInfoMap().Get(location.x+1, location.y).object==actionTarget
				) {
					return true;
			}

			return false;
		}

		void checkPerformAction() {
			if(actionTarget && actionType==ATTACK) {
				if(actionTarget->currentLocation().Distance(location)>sightRange()) {
					actionTarget=NULL;
					actionType=NONE;
				}
				else if(inHitRange()) {
					mAttackTarget = dynamic_cast<mechanics::Attackable *>(actionTarget);
					cancelMovement();
				}
				else {
					moveTo(actionTarget->currentLocation());
				}
			}
		}

		virtual float Damage(Attacker *attacker, BaseWeapon *weapon, float damage) {
			float d=Attackable::Damage(attacker, weapon, damage);
			
			if(d>0) {
				std::stringstream ss;
				ss<<"HP -"<<int(gge::utils::Round(d));
				log(ss.str());
			}

			return d;
		}

		const assets::Wildling &provider() const {
			return mProvider;
		}

	protected:
		virtual void setMoverLocation(gge::utils::Point pnt) {
			setLocation(pnt);
		}

		virtual gge::utils::Point getMoverLocation() const {
			return location;
		}

		const assets::Wildling &mProvider;

		int level;
		unsigned mNextAction;
		Den *mOwner;
		int laststep;

		Direction currentDirection;

		float mDamage;
		float mWeaponCooldown;
		float mRange;
		float mOvershootRange;

		float mDodgeBonus;
		float mParryBonus;
		float mBlockBonus;

		ActionType actionType;
		mechanics::Object *actionTarget;
		Attackable *mAttackTarget;

		unsigned mLastAttackTime;
		unsigned mAttackCooldown;
		unsigned mLastWeaponTime;

		unsigned mLastBlockTime;
		unsigned mBlockCooldown;
	};

}


