#pragma once

#include <vector>
#include <utility>
#include <Gaming/AI/Pathing.h>

#include "../Basic.h"
#include "../Assets/Constants.h"
#include "../Assets/Item.h"
#include "Attacker.h"
#include "Attackable.h"
#include "Object.h"
#include "Inventory.h"
#include "Mover.h"
#include "Item.h"
#include "Map.h"
#include "Weapons/BaseWeapon.h"
#include "FloatingLogger.h"

namespace mechanics {
	class TrainingBuilding;

	class Human : public Object, public Attacker, public Attackable, public Mover, public FloatingLogger {
	public:
		enum ActionType {
			NONE,
			ATTACK,
			MOVE,
			TAKE_ITEM,
			TRAIN,
		};

		Human(Animator &base, Animator &hair, OwnerType::Type owner, Gender::Type given_gender);

		virtual void locateTo(const gge::utils::Point &given_location);

		virtual void setLocation(const gge::utils::Point &given_location);

		virtual void removeFromMap();

		virtual std::string info();

		virtual float str() const;

		virtual float dex() const;

		virtual float acc() const;

		virtual float accMod() const;

		virtual float spe() const;

		virtual float speMod() const;

		virtual float intl() const;

		virtual float vit() const;

		virtual std::string name() const;

		virtual void name(std::string value);

		virtual std::string surname() const;

		virtual void surname(std::string value);

		virtual Gender::Type gender() const;

		virtual int level() const;

		virtual float xp() const;

		virtual Stat stat() const;

		virtual float maximumHP() const;

		virtual float stamina() const;

		virtual float staminaDrainModifier() const;

	private:
		virtual void stamina(float value);

	public:
		virtual void addStamina(float value);

		virtual float maximumStamina() const;

		virtual float movementSpeed() const;

		virtual float movementSpeedModifier() const;

		float baseAttackCooldown() const;

		virtual float attackCooldown() const;

		virtual void attackCooldown(float value);

		virtual Attackable *attackTarget() const;

		float sightRange(bool nighttime) const;

		virtual float sightRangeModifier() const;

		virtual bool ValidTarget(Object *object, bool ranged);

		virtual void CheckAttack();

		virtual float dodge() const;

		virtual float parry() const;

		virtual float block() const;

		virtual void levelUp();

		virtual void increaseXP(float value);

		virtual float XPReward() const;

		void startTraining(TrainingBuilding *building);

		virtual std::string title() const;
		virtual void Killed(Attacker *attacker, BaseWeapon *weapon);
		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 CalculateStagger(Attacker *attacker, BaseWeapon *weapon, float damage, float reducedDamage);
		virtual float staggerBonus(float damage, float reducedDamage);
		virtual float attackDamage();
		virtual float attackPrecision();

		Inventory &inventory();

		Item *inventory(assets::Item::Slot slot) const;

		std::vector<Item*> &getItemList();
		void addItem(Item &item, bool equip);
		void removeItem(Item *ptr);
		void equipItem(Item &item);
		void unequipItem(Item &item);
		void unequipItem(assets::Item::Slot slot);

		virtual void doAction(gge::utils::Point pnt);
		virtual void attackTo(mechanics::Object *target);
		virtual void moveStep();
		virtual void moveFinished();
		virtual void checkAction();
		virtual void checkPerformAction();
		virtual bool inHitRange();

		void checkTrain();

		virtual void setAnimation(Direction direction, std::string animationID, bool isLooping = true, float speed = 1);

		virtual void setDirection(Direction direction, std::string animationID);
		
		virtual void setMoveDirection(Direction direction);

		virtual void animSetMove(Direction direction, float speed);

		virtual void animStopMove(Direction direction);


		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;
		}

		float nextLevelXP() {
			float next=assets::constants::leveling::nextLvlBase;
			for(int i=0;i<mLevel;i++)
				next=assets::constants::leveling::nextLvlIncrement + next*assets::constants::leveling::nextLvlMultiplier;

			return next;
		}

		
		Animator &animatorHair;

		// animation related variables
		Direction currentDirection;
		std::string currentAnim;
		float currentSpeed;
		bool isLooping;

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

		virtual gge::utils::Point getMoverLocation() const;

		std::string mName;
		std::string mSurname;
		Gender::Type mGender;

		Stat mStat;
		int mLevel;
		float mXp;
		float mStamina;
		float mLastAttackTime;
		float mAttackCooldown;
		bool mIsControllable;

		unsigned lastRegen;

		std::vector<Item*> mItemList;

		void rebuildSight();
		void setSight();

		Inventory mInventory;
		float mDodgeBonus;
		float mParryBonus;
		float mBlockBonus;

		gge::utils::Point trainPoint;
		unsigned int lastTrainAttack;

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

}
