#include "Monster.h"
#include "DamageNumber.h"
#include "Crawler.h"
#include "Bullet.h"
#include "DEFINES.h"

INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_game
INCLUDE_BULLET_LIST

MonsterData::MonsterData(){}
MonsterData::MonsterData(MonsterName type,int hp,int atk,std::vector<AnimationState>animations,float moveSpd,float size,MonsterStrategy strategy,int collisionDmg):
	type(type),hp(hp),atk(atk),moveSpd(moveSpd),size(size),strategy(strategy),animations(animations),collisionDmg(collisionDmg){
}
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;
}
MonsterName MonsterData::GetType(){
	return type;
}
MonsterStrategy MonsterData::GetAIStrategy(){
	return strategy;
}

Monster::Monster(){}
Monster::Monster(vf2d pos,MonsterData data):
	pos(pos),hp(data.GetHealth()),maxhp(data.GetHealth()),atk(data.GetAttack()),moveSpd(data.GetMoveSpdMult()),size(data.GetSizeMult()),strategy(data.GetAIStrategy()),type(data.GetType()){
	bool firstAnimation=true;
	for(AnimationState&anim:data.GetAnimations()){
		animation.AddState(anim,ANIMATION_DATA[anim]);
		if(firstAnimation){
			animation.ChangeState(internal_animState,anim);
			firstAnimation=false;
		}
	}
	randomFrameOffset=(rand()%1000)/1000.f;
}
vf2d&Monster::GetPos(){
	return pos;
}
int Monster::GetHealth(){
	return hp;
}
int Monster::GetAttack(){
	return atk;
}
float Monster::GetMoveSpdMult(){
	return moveSpd;
}
float Monster::GetSizeMult(){
	return size;
}
Animate2D::Frame Monster::GetFrame(){
	return animation.GetFrame(internal_animState);
}
void Monster::UpdateAnimation(AnimationState state){
	animation.ChangeState(internal_animState,state);
}
void Monster::PerformJumpAnimation(){
	switch(type){
		case SLIME_GREEN:{
			animation.ChangeState(internal_animState,AnimationState::GREEN_SLIME_JUMP);
		}break;
		case SLIME_BLUE:{
			animation.ChangeState(internal_animState,AnimationState::BLUE_SLIME_JUMP);
		}break;
		case SLIME_RED:{
			animation.ChangeState(internal_animState,AnimationState::RED_SLIME_JUMP);
		}break;
	}
}
void Monster::PerformShootAnimation(){
	switch(type){
	case SLIME_GREEN:{
		animation.ChangeState(internal_animState,AnimationState::GREEN_SLIME_SPIT);
	}break;
	case SLIME_BLUE:{
		animation.ChangeState(internal_animState,AnimationState::BLUE_SLIME_SPIT);
	}break;
	case SLIME_RED:{
		animation.ChangeState(internal_animState,AnimationState::RED_SLIME_SPIT);
	}break;
	}
}
void Monster::SetX(float x){
	if(x-12*size>0&&x+12*size<game->WORLD_SIZE.x*24){
		pos.x=x;
	} else {
		pos.x=std::min(game->WORLD_SIZE.x*24-12*size,std::max(12*size,pos.x));
	}
}
void Monster::SetY(float y){
	if(y-12*size>0&&y+12*size<game->WORLD_SIZE.y*24){
		pos.y=y;
	} else {
		pos.y=std::min(game->WORLD_SIZE.y*24-12*size,std::max(12*size,pos.y));
	}
}
bool Monster::Update(float fElapsedTime){
	if(IsAlive()){
		for(Monster&m:MONSTER_LIST){
			if(&m==this)continue;
			if(geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(m.GetPos(),12*m.GetSizeMult()/2))){
				m.Collision(*this);
				geom2d::line line(pos,m.GetPos());
				float dist = line.length();
				m.SetPosition(line.rpoint(dist*1.1));
				if(m.IsAlive()){
					vel=line.vector().norm()*-128;
				}
			}
		}
		if(!game->GetPlayer().HasIframes()&&geom2d::overlaps(geom2d::circle(pos,12*size/2),geom2d::circle(game->GetPlayer().GetPos(),12*game->GetPlayer().GetSizeMult()/2))){
			geom2d::line line(pos,game->GetPlayer().GetPos());
			float dist = line.length();
			SetPosition(line.rpoint(-0.1));
			vel=line.vector().norm()*-128;
		}
		if(state==NORMAL){
			if(game->GetPlayer().GetX()>pos.x){
				facingDirection=RIGHT;
			} else {
				facingDirection=LEFT;
			}
		}
		switch(strategy){
			case RUN_TOWARDS:{
				targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime);
				if(targetAcquireTimer==0){
					targetAcquireTimer=3;
					target=geom2d::line(pos,game->GetPlayer().GetPos()).upoint(1.2);
					state=MOVE_TOWARDS;
				}
				if(state==MOVE_TOWARDS&&geom2d::line(pos,target).length()>100*fElapsedTime*moveSpd){
					SetPosition(pos+geom2d::line(pos,target).vector().norm()*100*fElapsedTime*moveSpd);
					PerformJumpAnimation();
				} else {
					if(state==MOVE_TOWARDS){
						state=NORMAL;//Revert state once we've finished moving towards target.
						UpdateAnimation(MONSTER_DATA[type].GetAnimations()[0]);
					}
				}
			}break;
			case SHOOT_AFAR:{
				targetAcquireTimer=std::max(0.f,targetAcquireTimer-fElapsedTime);
				attackCooldownTimer=std::max(0.f,attackCooldownTimer-fElapsedTime);
				if(queueShotTimer>0){
					queueShotTimer-=fElapsedTime;
					if(queueShotTimer<0){
						queueShotTimer=0;
						BULLET_LIST.push_back(Bullet(pos+vf2d{0,-4},geom2d::line(pos+vf2d{0,-4},game->GetPlayer().GetPos()).vector().norm()*24*3.f,2,atk,{75/2,162/2,225/2}));
					}
				}
				geom2d::line line(pos,game->GetPlayer().GetPos());
				if(targetAcquireTimer==0&&queueShotTimer==0){
					targetAcquireTimer=1;
					if(line.length()<24*6){
						target=line.upoint(-1.2);
						if(pos.x-12*size>1&&pos.x+12*size<game->WORLD_SIZE.x*24-1&&
							pos.y-12*size>1&&pos.y+12*size<game->WORLD_SIZE.y*24-1){
							state=MOVE_AWAY;
						} else 
						if(pos.x-12*size<=1||pos.x+12*size>=game->WORLD_SIZE.x*24-1){
							geom2d::line moveTowardsLine=geom2d::line(pos,target);
							if(abs(moveTowardsLine.vector().norm().y)>=0.5){
								state=MOVE_AWAY;
							} else {
								state=NORMAL;
							}
						} else 
						if(pos.y-12*size<=1||pos.y+12*size>=game->WORLD_SIZE.y*24-1){
							geom2d::line moveTowardsLine=geom2d::line(pos,target);
							if(abs(moveTowardsLine.vector().norm().x)>=0.5){
								state=MOVE_AWAY;
							} else {
								state=NORMAL;
							}
						}
					} else
					if(line.length()>24*7){
						target=line.upoint(1.2);
						state=MOVE_TOWARDS;
					} else {
						state=NORMAL;
					}
				}
				geom2d::line moveTowardsLine=geom2d::line(pos,target);
				switch(state){
					case MOVE_TOWARDS:{
						if(moveTowardsLine.length()>1){
							SetPosition(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*moveSpd);
						}
						if(line.length()<=24*7){
							state=NORMAL;
						}
						if(moveTowardsLine.vector().x>0){
							facingDirection=RIGHT;
						} else {
							facingDirection=LEFT;
						}
						PerformJumpAnimation();
					}break;
					case MOVE_AWAY:{
						if(moveTowardsLine.length()>1){
							SetPosition(pos+moveTowardsLine.vector().norm()*100*fElapsedTime*moveSpd);
						}
						if(line.length()>=24*6){
							state=NORMAL;
						}
						if(moveTowardsLine.vector().x>0){
							facingDirection=RIGHT;
						} else {
							facingDirection=LEFT;
						}
						PerformJumpAnimation();
					}break;
					default:{
						if(attackCooldownTimer==0){
							attackCooldownTimer=1;
							queueShotTimer=0.7;
							PerformShootAnimation();
						}
					}
				}
			}break;
		}
		if(vel.x>0){
			vel.x=std::max(0.f,vel.x-friction*fElapsedTime);
		} else {
			vel.x=std::min(0.f,vel.x+friction*fElapsedTime);
		}
		if(vel.y>0){
			vel.y=std::max(0.f,vel.y-friction*fElapsedTime);
		} else {
			vel.y=std::min(0.f,vel.y+friction*fElapsedTime);
		}
		SetX(pos.x+vel.x*fElapsedTime);
		SetY(pos.y+vel.y*fElapsedTime);
	}
	if(hp<=0){
		deathTimer+=fElapsedTime;
		if(deathTimer>3){
			return false;
		}
	}
	animation.UpdateState(internal_animState,randomFrameOffset+fElapsedTime);
	randomFrameOffset=0;
	return true;
}
Key Monster::GetFacingDirection(){
	return facingDirection;
}
void Monster::Draw(){
	if(GetFacingDirection()==RIGHT){
		game->view.DrawPartialDecal((GetPos()+vf2d{float(GetFrame().GetSourceRect().size.x),0}*GetSizeMult())-vf2d{12,12}*GetSizeMult(),GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult()*-1,GetSizeMult()));
	} else {
		game->view.DrawPartialDecal(GetPos()-vf2d{12,12}*GetSizeMult(),GetFrame().GetSourceImage()->Decal(),GetFrame().GetSourceRect().pos,GetFrame().GetSourceRect().size,vf2d(GetSizeMult(),GetSizeMult()));
	}
}
void Monster::Collision(Player&p){
	if(MONSTER_DATA[type].GetCollisionDmg()>0){
		p.Hurt(MONSTER_DATA[type].GetCollisionDmg());
	}
	Collision();
}
void Monster::Collision(Monster&m){
	Collision();
}
void Monster::Collision(){
	if(strategy==RUN_TOWARDS&&state==MOVE_TOWARDS){
		state=NORMAL;
	}
}
void Monster::SetVelocity(vf2d vel){
	this->vel=vel;
}
void Monster::SetPosition(vf2d pos){
	SetX(pos.x);
	SetY(pos.y);
}
AnimationState Monster::GetDeathAnimationName(){
	switch(type){
		case SLIME_GREEN:{
			return AnimationState::GREEN_SLIME_DIE;
		}
		case SLIME_BLUE:{
			return AnimationState::BLUE_SLIME_DIE; 
		}
		case SLIME_RED:{
			return AnimationState::RED_SLIME_DIE;
		}
		default:{
			return AnimationState::WARRIOR_IDLE_S;
		}
	}
} 
bool Monster::Hurt(int damage){
	if(hp<=0) return false;
	hp=std::max(0,hp-damage);
	DAMAGENUMBER_LIST.push_back(DamageNumber(pos,damage));
	if(hp<=0){
		animation.ChangeState(internal_animState,GetDeathAnimationName());
	}
	return true;
}

bool Monster::IsAlive(){
	return hp>0;
}
vf2d&Monster::GetTargetPos(){
	return target;
}

MonsterSpawner::MonsterSpawner(){}
MonsterSpawner::MonsterSpawner(vf2d pos,int range,std::vector<std::pair<MonsterName,vf2d>>monsters):
	pos(pos),range(range),monsters(monsters){
}
bool MonsterSpawner::SpawnTriggered(){
	return triggered;
}
int MonsterSpawner::GetRange(){
	return range;
}
vf2d MonsterSpawner::GetPos(){
	return pos;
}
void MonsterSpawner::SetTriggered(bool trigger,bool spawnMonsters){
	triggered=trigger;
	if(spawnMonsters){
		for(std::pair<MonsterName,vf2d>&monsterInfo:monsters){
			MONSTER_LIST.push_back(Monster(pos+monsterInfo.second,MONSTER_DATA[monsterInfo.first]));
		}
	}
}