#include "Unit.h"
#include "Constant.h"
#include "olcUTIL_Geometry2D.h"
#include "TileManager.h"

BasicUnit::BasicUnit(vf2d pos,Renderable&img,bool friendly)
	:Unit({
		{HEALTH,4},
		{RANGE,2},
		{ATKSPD,2},
		{MOVESPD,3},
		{PROCEDURE,1},
		},pos,img,friendly){}


void BasicUnit::Attack(Unit&victim){
	victim<<=1;
}

BasicUnit2::BasicUnit2(vf2d pos,Renderable&img,bool friendly)
	:Unit({
		{RANGE,2},
		{ATKSPD,2},
		{MOVESPD,3},
		{PROCEDURE,1},
		{HEALTH,4},
		},pos,img,friendly){}

void BasicUnit2::Attack(Unit&victim){
	
}



Unit::Unit(std::vector<Memory>memory,vf2d pos,Renderable&img,bool friendly)
:pos(pos),ghostPos(pos),img(img),friendly(friendly){
	int marker=0;
	for(Memory&mem:memory){
		for(int i=0;i<mem.size;i++){
			this->memory.push_back(true);
			this->ghostMemory.push_back(true);
		}
		switch(mem.type){
		case HEALTH:{
			health.index=marker;
			health.size=mem.size;
		}break;	
		case RANGE:{
			range.index=marker;
			range.size=mem.size;
		}break;	
		case ATKSPD:{
			atkSpd.index=marker;
			atkSpd.size=mem.size;
		}break;	
		case MOVESPD:{
			moveSpd.index=marker;
			moveSpd.size=mem.size;
		}break;	
		case PROCEDURE:{
			procedure.index=marker;
			procedure.size=mem.size;
		}break;	
		}
		marker+=mem.size;
	}
}



void Unit::Draw(TileTransformedView&game){
	game.DrawRotatedDecal(ghostPos,img.Decal(),0,img.Sprite()->Size()/2,{1,1},friendly?Pixel{192,192,255}:Pixel{255,192,192});
	if(IsSelected()){
		game.DrawRotatedDecal(ghostPos,CONSTANT::SELECTION_CIRCLE.Decal(),0,CONSTANT::SELECTION_CIRCLE.Sprite()->Size()/2,vf2d(img.Sprite()->Size())/CONSTANT::SELECTION_CIRCLE.Sprite()->Size(),WHITE);
	}
}

void Unit::DrawHud(TileTransformedView&game){
	int initialBarX=ghostPos.x-GetMemorySize()/2*CONSTANT::BAR_SQUARE_SIZE.x;
	int initialBarY=ghostPos.y-CONSTANT::BAR_SQUARE_SIZE.y-img.Sprite()->height/2-2;
	Pixel col=0;


	auto CheckColor=[&](int i,Pixel&col){
		if(health.index==i){
			col=CONSTANT::HEALTH_COLOR;
		}
		if(range.index==i){
			col=CONSTANT::RANGE_COLOR;
		}
		if(atkSpd.index==i){
			col=CONSTANT::ATKSPD_COLOR;
		}
		if(moveSpd.index==i){
			col=CONSTANT::MOVESPD_COLOR;
		}
		if(procedure.index==i){
			col=CONSTANT::PROCEDURE_COLOR;
		}
	};

	for(int i=0;i<GetMemorySize();i++){
		CheckColor(i,col);

		game.FillRectDecal({float(initialBarX)+i*CONSTANT::BAR_SQUARE_SIZE.x,
			float(initialBarY)},CONSTANT::BAR_SQUARE_SIZE,ghostMemory[i]?col:col/4);
		game.DrawRectDecal({float(initialBarX)+i*CONSTANT::BAR_SQUARE_SIZE.x,
			float(initialBarY)},CONSTANT::BAR_SQUARE_SIZE,BLACK);
	}
}

int Unit::GetBits(Marker&m){
	int activeBits=0;
	for(int i=0;i<m.size;i++){
		if(memory[i+m.index]){
			activeBits++;
		}
	}
	return activeBits;
}

int Unit::GetHealth(){
	return GetBits(health);
}

int Unit::GetRange(){
	return GetBits(range);
}

int Unit::GetAtkSpd(){
	return GetBits(atkSpd);
}

int Unit::GetMoveSpd(){
	return GetBits(moveSpd);
}

int Unit::GetProcedure(){
	return GetBits(procedure);
}

int Unit::GetMemorySize(){
	return memory.size();
}

void Unit::Update(float fElapsedTime){
	if(!target.expired()){
		auto ptrTarget=target.lock();
		if(!InRange(ptrTarget)){
			SetPos(GetPos()+(ptrTarget->GetPos()-pos).norm()*GetMoveSpd()*24*fElapsedTime);
		} else {
			//TODO Attack here.
		}
	} else 
	if(targetLoc!=CONSTANT::UNSELECTED){
		float dist=geom2d::line<float>(pos,targetLoc).length();
		if(dist>24){
			SetPos(GetPos()+(targetLoc-pos).norm()*GetMoveSpd()*24*fElapsedTime);
		}
	}

	if(!IsFriendly()){
		if(changeDirTimer==0){
			changeDirTimer=rand()%30;
			switch(rand()%4){
				case 0:{
					movementVel={16,0};
				}break;
				case 1:{
					movementVel={0,16};
				}break;
				case 2:{
					movementVel={-16,0};
				}break;
				case 3:{
					movementVel={0,-16};
				}break;
			}
		}
		SetPos(GetPos()+movementVel*fElapsedTime);
		changeDirTimer=std::max(0.f,changeDirTimer-fElapsedTime);
	}

	reloadTimer=std::max(0.f,reloadTimer-fElapsedTime);
}

std::vector<bool> operator <<(Unit&u,const int n){
	std::vector<bool>tempMem=u.memory;
	for(int i=0;i<u.GetMemorySize()-1;i++){
		tempMem[i]=tempMem[i+1];
	}
	tempMem[u.GetMemorySize()-1]=0;
	return tempMem;
}

std::vector<bool> operator >>(Unit&u,const int n){
	std::vector<bool>tempMem=u.memory;
	for(int i=1;i<u.GetMemorySize();i++){
		tempMem[i]=tempMem[i-1];
	}
	tempMem[0]=0;
	return tempMem;
}

bool Unit::IsFriendly(){
	return friendly;
}


bool Unit::IsSelected(){
	return selected;
}

void Unit::Select(){
	selected=true;
}

void Unit::Deselect(){
	selected=false;
}

vf2d Unit::GetPos(){
	return pos;
}

bool Unit::IsDead(){
	return dead;
}

vf2d Unit::GetUnitSize(){
	return img.Sprite()->Size();
}

void Unit::SetTargetUnit(std::weak_ptr<Unit>target){
	this->target=target;
	this->targetLoc=CONSTANT::UNSELECTED;
}

void Unit::SetTargetLocation(vf2d targetLoc){
	this->target.reset();
	this->targetLoc=targetLoc;
}

bool Unit::InRange(std::shared_ptr<Unit>target){
	float dist=geom2d::line(GetPos(),target->GetPos()).length();
	return dist<24*(GetRange()+1);
}

bool Unit::InRange(vf2d pos){
	float dist=geom2d::line(GetPos(),pos).length();
	return dist<24*(GetRange()+1);
}

void Unit::SetPos(vf2d newPos){
	pos=newPos;
	if(!InFogOfWar()){
		ghostPos=pos;
	}
}

void Unit::AttemptAttack(Unit*unit){
	if(reloadTimer>0)return;
	Unit*finalTarget=nullptr;
	if(unit!=nullptr){
		finalTarget=unit;
		if(!target.expired()){
			auto ptrTarget=target.lock();
			if(InRange(ptrTarget)){
				finalTarget=ptrTarget.get();
			}
		}
	}
	if(finalTarget!=nullptr){
		if(InRange(finalTarget->GetPos())){
			_Attack(finalTarget); //Call the parent function first, followed by the child.
		}
	}
}

void Unit::_Attack(Unit*finalTarget){
	Attack(*finalTarget);
	reloadTimer=1.f/GetAtkSpd();
}

bool Unit::InFogOfWar(){
	return TileManager::visibleTiles.count(GetPos()/96)==0;
}

bool Unit::GhostInFogOfWar(){
	return TileManager::visibleTiles.count(ghostPos/96)==0;
}

void Unit::HideGhost(){
	ghostPos={99999,-99999};
}

vf2d Unit::GetGhostPos(){
	return ghostPos;
}