#include "olcPixelGameEngine.h"
#include "Monster.h"
#include "Animation.h"
#include "config.h"
#include "DEFINES.h"
#include "safemap.h"

INCLUDE_DATA
INCLUDE_STRATEGY_DATA
INCLUDE_ANIMATION_DATA

std::map<int,MonsterData>MONSTER_DATA;
safemap<std::string,MonsterData*>MONSTER_NAME_DATA;

MonsterData::MonsterData()
:atk(0),collisionDmg(0),hp(0),id(0),moveSpd(0),size(0),strategy(0){}
MonsterData::MonsterData(int id,std::string name,int hp,int atk,std::vector<std::string>animations,float moveSpd,float size,int strategy,int collisionDmg):
	id(id),name(name),hp(hp),atk(atk),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),collisionDmg(collisionDmg){}

void MonsterData::InitializeMonsterData(){
	int id=0;
	while(DATA["Monsters"].HasProperty(std::to_string(id))){
		std::string MonsterName=DATA["Monsters"][std::to_string(id)]["DisplayName"].GetString();
		std::vector<std::string>animations{
			MonsterName+"_IDLE",	
			MonsterName+"_JUMP",	
			MonsterName+"_SPIT",	
			MonsterName+"_DIE",	
		};

		MonsterData::imgs[id]=new Renderable();
		MonsterData::imgs[id]->Load("assets/monsters/"+MonsterName+".png");

		for(int i=0;i<animations.size();i++){
			std::string animationConfigName="";
			std::string imgName="";
			switch(i){
				case 0:{
					animationConfigName="Idle";
					imgName="_IDLE";
				}break;
				case 1:{
					animationConfigName="Jump";
					imgName="_JUMP";
				}break;
				case 2:{
					animationConfigName="Shoot";
					imgName="_SPIT";
				}break;
				case 3:{
					animationConfigName="Death";
					imgName="_DIE";
				}break;
			}
			Animate2D::Style style=Animate2D::Style::Repeat;
			if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="Repeat"){
				style=Animate2D::Style::Repeat;
			} else
			if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="OneShot"){
				style=Animate2D::Style::OneShot;
			} else
			if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="PingPong"){
				style=Animate2D::Style::PingPong;
			} else 
			if(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetString(2)=="Reverse"){
				style=Animate2D::Style::Reverse;
			}

			auto CreateHorizontalAnimationSequence=[&](Renderable&img,int frameCount,vf2d size,std::string state,int row,AnimationData data={}){
				Animate2D::FrameSequence anim(data.frameDuration,data.style);
				for(int i=0;i<frameCount;i++){
					anim.AddFrame({&img,{{int(i*size.x),int(row*size.y)},size}});
				}
				ANIMATION_DATA[state]=anim;
			};

			int frameCount = DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetInt(0);
			vf2d frameSize = vf2d{float(DATA["Monsters"][std::to_string(id)]["SheetFrameSize"].GetInt(0)),float(DATA["Monsters"][std::to_string(id)]["SheetFrameSize"].GetInt(1))};
			CreateHorizontalAnimationSequence(*MonsterData::imgs[id],frameCount,frameSize,MonsterName+imgName,i,AnimationData{float(DATA["Monsters"][std::to_string(id)][animationConfigName+"Animation"].GetReal(1)),style});
		}

		//Add additional custom animations defined in the config.
		int animationCounter=0;
		while(DATA["Monsters"][std::to_string(id)].HasProperty("ANIMATION["+std::to_string(animationCounter)+"]")){
			animations.push_back(DATA["Monsters"][std::to_string(id)]["ANIMATION["+std::to_string(animationCounter)+"]"].GetString());
			animationCounter++;
		}

		MonsterData monster(
			id,
			MonsterName,
			DATA["Monsters"][std::to_string(id)]["Health"].GetInt(),
			DATA["Monsters"][std::to_string(id)]["Attack"].GetInt(),
			animations,
			DATA["Monsters"][std::to_string(id)]["MoveSpd"].GetReal()/100,
			DATA["Monsters"][std::to_string(id)]["Size"].GetReal()/100,
			STRATEGY_DATA[DATA["Monsters"][std::to_string(id)]["Strategy"].GetString()],
			DATA["Monsters"][std::to_string(id)]["CollisionDmg"].GetInt()
		);

		MONSTER_DATA[id]=monster;
		MONSTER_NAME_DATA[MonsterName]=&MONSTER_DATA[id];

		id++;
	}

	MONSTER_NAME_DATA.SetInitialized();
}
int MonsterData::GetHealth(){
	return hp;
}
int MonsterData::GetAttack(){
	return atk;
}
float MonsterData::GetMoveSpdMult(){
	return moveSpd;
}
float MonsterData::GetSizeMult(){
	return size;
}
int MonsterData::GetCollisionDmg(){
	return collisionDmg;
}
int MonsterData::GetID(){
	return id;
}
int MonsterData::GetAIStrategy(){
	return strategy;
}

std::string MonsterData::GetIdleAnimation(){
	return animations[IDLE];
}
std::string MonsterData::GetJumpAnimation(){
	return animations[JUMP];
}
std::string MonsterData::GetShootAnimation(){
	return animations[SHOOT];
}
std::string MonsterData::GetDeathAnimation(){
	return animations[DEATH];
}