#pragma once
#include "olcPixelGameEngine.h"
#include "Animation.h"
#include "State.h"
#include "Buff.h"
#include "olcUTIL_Animate2D.h"
#include "DamageNumber.h"

struct Player;

enum MonsterStrategy{
	RUN_TOWARDS,
	SHOOT_AFAR
};

enum MonsterName{
	SLIME_GREEN,
	SLIME_BLUE,
	SLIME_RED,
	SLIME_YELLOW,


	///////////////////////////////////////////////////////////////////////////////////////////////////////////
	/*//*/END,//Used for detecting the end of the list, DO NOT USE OR TOUCH. Add all monsters above this//*//*/
	///////////////////////////////////////////////////////////////////////////////////////////////////////////
};

struct MonsterData{
	private:
	int hp;
	int atk;
	float moveSpd;//1.0=100%
	float size;
	std::vector<AnimationState> animations;
	MonsterStrategy strategy;
	MonsterName type;
	int collisionDmg;
	AnimationState jumpAnimation=AnimationState::WARRIOR_IDLE_S;
	AnimationState shootAnimation=AnimationState::WARRIOR_IDLE_S;
	AnimationState deathAnimation=AnimationState::WARRIOR_IDLE_S;
	public:
	MonsterData();
	//When specifying animations, the first one will become the default animation. The last becomes the death animation.
	MonsterData(MonsterName type,int hp,int atk,std::vector<AnimationState>animations,AnimationState jumpAnimation,AnimationState shootAnimation,AnimationState deathAnimation,float moveSpd=1.0f,float size=1.0f,MonsterStrategy strategy=RUN_TOWARDS,int collisionDmg=0);
	int GetHealth();
	int GetAttack();
	float GetMoveSpdMult();
	float GetSizeMult();
	MonsterName GetType();
	MonsterStrategy GetAIStrategy();
	int GetCollisionDmg();
	AnimationState GetJumpAnimation();
	AnimationState GetShootAnimation();
	AnimationState GetDeathAnimation();
	std::vector<AnimationState>GetAnimations(){
		return animations;
	}
};

struct Monster{
	private:
	vf2d pos;
	vf2d vel={0,0};
	float friction=400;
	vf2d target={0,0};
	float targetAcquireTimer=0;
	int hp,maxhp;
	int atk;
	float moveSpd;
	float size;
	float attackCooldownTimer=0;
	float queueShotTimer=0;
	Key facingDirection;
	MonsterStrategy strategy;
	State state=State::NORMAL;
	Animate2D::Animation<AnimationState>animation;
	Animate2D::AnimationState internal_animState;
	float randomFrameOffset=0.f;
	float deathTimer=0.f;
	MonsterName type;
	std::vector<Buff>buffList;
	AnimationState GetDeathAnimationName();
	bool hasHitPlayer=false;
	bool canMove=true; //Set to false when stuck due to collisions.
	bool upperLevel=false;
	vf2d pathTarget={};
	std::vector<vf2d>path;
	int pathIndex=0;
	float lastHitTimer=0;
	std::shared_ptr<DamageNumber>damageNumberPtr;
protected:
	public:
	Monster()=delete;
	Monster(vf2d pos,MonsterData data,bool upperLevel=false);
	vf2d&GetPos();
	int GetHealth();
	int GetAttack();
	float GetMoveSpdMult();
	float GetSizeMult();
	Animate2D::Frame GetFrame();
	void UpdateAnimation(AnimationState state);
	bool Update(float fElapsedTime);
	//Returns true when damage is actually dealt. Provide whether or not the attack is on the upper level or not. Monsters must be on the same level to get hit by it. (there is a death check and level check here.)
	//If you need to hurt multiple enemies try Crawler::HurtEnemies()
	bool Hurt(int damage,bool onUpperLevel);
	bool IsAlive();
	vf2d&GetTargetPos();
	Key GetFacingDirection();
	void Draw();
	void Collision(Player*p);
	void Collision(Monster&p);
	void Collision();
	void SetVelocity(vf2d vel);
	//Returns false if the monster could not be moved to the requested location due to collision.
	bool SetPosition(vf2d pos);
	//Returns false if the monster could not be moved to the requested location due to collision.
	bool SetX(float x);
	//Returns false if the monster could not be moved to the requested location due to collision.
	bool SetY(float y);
	void PerformJumpAnimation();
	void PerformShootAnimation();
	bool OnUpperLevel();
	void Moved();
	void StartPathfinding(float pathingTime);
	void PathAroundBehavior(float fElapsedTime);
	void AddBuff(BuffType type,float duration,float intensity);
	std::vector<Buff>GetBuffs(BuffType buff);
};

struct MonsterSpawner{
	private:
	vf2d pos;
	vf2d range;
	std::vector<std::pair<MonsterName,vf2d>>monsters;
	bool triggered=false;
	bool upperLevel=false;
	public:
	MonsterSpawner();
	//For the monster list, the second pair item is the position relative to the spawner to spawn the monster.
	MonsterSpawner(vf2d pos,vf2d range,std::vector<std::pair<MonsterName,vf2d>>MONSTER_LIST,bool upperLevel=false);
	bool SpawnTriggered();
	vf2d GetRange();
	vf2d GetPos();
	bool DoesUpperLevelSpawning();
	void SetTriggered(bool trigger,bool spawnMonsters=true);
	friend std::ostream&operator<<(std::ostream&os,MonsterSpawner&rhs);
};