#define OLC_PGE_APPLICATION
#define OLC_SOUNDWAVE
#define OLC_PGEX_TRANSFORMEDVIEW
#define AUDIO_LISTENER_IMPLEMENTATION
#define AUDIO_SOURCE_IMPLEMENTATION
#define OLC_PGEX_QUICKGUI
#include "olcUTIL_Geometry2D.h"
#include "TileManager.h"
#include "util.h"

#include "VirusAttack.h"

VirusAttack::VirusAttack()
{
	// Name your application
	sAppName = "olcCodeJam 2023 Entry";
}

void VirusAttack::InitializeImages(){
	auto LoadImage=[&](Image img,std::string filepath,bool filter=false,bool clamp=true){
		IMAGES[img]=std::make_unique<Renderable>();
		IMAGES[img]->Load(filepath,nullptr,filter,clamp);
	};
	LoadImage(TILE,"assets/tile.png",false,false);
	LoadImage(MINIMAP_HUD,"assets/minimap_hud.png");
	LoadImage(OUTLINE,"assets/outline.png");
	LoadImage(VIRUS_IMG1,"assets/unit.png");
	LoadImage(SELECTION_CIRCLE,"assets/selection_circle.png");
	LoadImage(MEMORY_COLLECTION_POINT,"assets/memory_collection_point.png");
	LoadImage(LEFT_SHIFTER,"assets/left_shifter.png");
	LoadImage(RIGHT_SHIFTER,"assets/right_shifter.png");
	LoadImage(BIT_RESTORER,"assets/bit_restorer.png");
	LoadImage(MEMORY_SWAPPER,"assets/memory_swapper.png");
	LoadImage(CORRUPTER,"assets/corrupter.png");
	LoadImage(UNIT_ALLOCATOR,"assets/shell.png");
	LoadImage(RAM_BANK,"assets/ram_bank.png");
	LoadImage(RANGE_INDICATOR,"assets/range_indicator.png");
	LoadImage(DOWN_ARROW,"assets/down_arrow.png");
	LoadImage(RED_X,"assets/red_x.png");
	LoadImage(RLD,"assets/rld.png");
	LoadImage(PRC,"assets/prc.png");
	LoadImage(RNG,"assets/rng.png");
	LoadImage(SPD,"assets/spd.png");
	LoadImage(TARGETING_LINE,"assets/targetLine.png");
	LoadImage(ATTACKING_LINE,"assets/attackLine.png");
	LoadImage(RLD_ICON,"assets/rld_icon.png");
	LoadImage(PRC_ICON,"assets/prc_icon.png");
	LoadImage(RNG_ICON,"assets/rng_icon.png");
	LoadImage(SPD_ICON,"assets/spd_icon.png");
	LoadImage(RESOURCE,"assets/material.png");
	LoadImage(MEMORY_COLLECTION_POINT_HIGHLIGHT,"assets/memory_collection_point_highlight.png");
}

bool VirusAttack::OnUserCreate(){
	SetPixelMode(Pixel::MASK);

	game.Initialise(GetScreenSize());

	InitializeImages();

	IMAGES[MINIMAP_OUTLINE]=std::make_unique<Renderable>();
	IMAGES[MINIMAP_OUTLINE]->Create(64,64);

	IMAGES[MATRIX]=std::make_unique<Renderable>();
	IMAGES[MATRIX]->Create(64,64,false,false);
	IMAGES[MATRIX]->Sprite()->SetSampleMode(Sprite::PERIODIC);

	AL.AudioSystemInit();
	InitializeSounds();

	InitializeUnitCreationGUI();

	units.push_back(std::make_unique<LeftShifter>(this,vf2d{128,128},IMAGES,true));
	units.push_back(std::make_unique<RightShifter>(this,vf2d{129,129},IMAGES,true));
	units.push_back(std::make_unique<BitRestorer>(this,vf2d{130,130},IMAGES,true));
	units.push_back(std::make_unique<BitRestorer>(this,vf2d{130,140},IMAGES,true));
	units.push_back(std::make_unique<MemorySwapper>(this,vf2d{131,131},IMAGES,true));
	units.push_back(std::make_unique<Corrupter>(this,vf2d{132,132},IMAGES,true));
	units.push_back(std::make_unique<MemoryAllocator>(this,vf2d{133,133},IMAGES,true));
	units.push_back(std::make_unique<RAMBank>(this,vf2d{134,134},IMAGES,true));
	

	for(int i=0;i<5;i++){
		collectionPoints.push_back(std::make_unique<CollectionPoint>(this,vf2d{32.f+48*i,32.f},0,*IMAGES[MEMORY_COLLECTION_POINT],MemoryType(i)));
		collectionPoints.push_back(std::make_unique<CollectionPoint>(this,vf2d{32.f,32.f+48*i},-PI/2,*IMAGES[MEMORY_COLLECTION_POINT],MemoryType(i)));
	}

	units.push_back(std::make_unique<RAMBank>(this,vf2d{1200,1200},IMAGES,false));

	units.push_back(std::make_unique<RightShifter>(this,vf2d{1260,1200},IMAGES,false));
	units.push_back(std::make_unique<RightShifter>(this,vf2d{360,300},IMAGES,false));
	units.push_back(std::make_unique<RightShifter>(this,vf2d{361,300},IMAGES,false));

	return true;
}

void VirusAttack::InitializeUnitCreationGUI(){
	unitCreationList.colNormal = olc::DARK_GREEN;
	unitCreationList.colHover = olc::GREEN;
	unitCreationList.colClick = olc::YELLOW;
	unitCreationList.colDisable = olc::DARK_GREY;
	unitCreationList.colBorder = olc::YELLOW;
	unitCreationList.colText = olc::WHITE;
	leftShifterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[LEFT_SHIFTER],{0.5,0.5},{16.f+32*0,float(ScreenHeight()-32)},{20,20});
	rightShifterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[RIGHT_SHIFTER],{0.5,0.5},{16.f+32*1,float(ScreenHeight()-32)},{20,20});
	bitRestorerButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[BIT_RESTORER],{0.5,0.5},{16.f+32*2,float(ScreenHeight()-32)},{20,20});
	memorySwapperButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[MEMORY_SWAPPER],{0.5,0.5},{16.f+32*3,float(ScreenHeight()-32)},{20,20});
	corrupterButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[CORRUPTER],{0.5,0.5},{16.f+32*4,float(ScreenHeight()-32)},{20,20});
	platformButton=new QuickGUI::ImageButton(unitCreationList,*IMAGES[UNIT_ALLOCATOR],{0.5,0.5},{16.f+32*5,float(ScreenHeight()-32)},{20,20});
}

void VirusAttack::InitializeSounds(){
	int soundIndex=0;
	auto LoadSound=[&](Sound sound,std::string soundFilename){
		SOUNDS[sound]=std::make_unique<Audio>();
		SOUNDS[sound]->AL=&AL;
		SOUNDS[sound]->LoadAudioSample(soundIndex,std::string("./assets/"+soundFilename).c_str());
		soundIndex++;
	};

	LoadSound(Sound::HUM,"machine2.wav");
}

bool VirusAttack::UnitCreationClickHandled(){
	if(leftShifterButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(LeftShifter::resourceCost)){
				std::unique_ptr<LeftShifter>buildUnit=std::make_unique<LeftShifter>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	if(rightShifterButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(RightShifter::resourceCost)){
				std::unique_ptr<RightShifter>buildUnit=std::make_unique<RightShifter>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	if(bitRestorerButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(BitRestorer::resourceCost)){
				std::unique_ptr<BitRestorer>buildUnit=std::make_unique<BitRestorer>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	if(memorySwapperButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(MemorySwapper::resourceCost)){
				std::unique_ptr<MemorySwapper>buildUnit=std::make_unique<MemorySwapper>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	if(corrupterButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(Corrupter::resourceCost)){
				std::unique_ptr<Corrupter>buildUnit=std::make_unique<Corrupter>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	if(platformButton->bPressed){
		for(auto&u:units){
			if(u->IsSelected()&&u->IsAllocator()&&CanAfford(MemoryAllocator::resourceCost)){
				std::unique_ptr<MemoryAllocator>buildUnit=std::make_unique<MemoryAllocator>(this,u->GetPos(),IMAGES,u->IsFriendly());
				u->SetBuildUnit(CONSTANT::UNIT_BUILD_TIME,std::move(buildUnit));
			}
		}
		return true;
	}
	return false;	
};

void VirusAttack::HandleDraggingSelection(){
	auto NotClickingOnMinimap=[&](){return !(GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64);};
	bool allocatorSelected=false;
	for(auto&u:units){
		u->UpdateGUIState(game,player_resources);
		if(u->IsSelected()&&u->IsAllocator()){
			allocatorSelected=true;
		}
	}
	unitCreationList.DisplayAllControls(allocatorSelected);
	leftShifterButton->Enable(CanAfford(LeftShifter::resourceCost));
	rightShifterButton->Enable(CanAfford(RightShifter::resourceCost));
	bitRestorerButton->Enable(CanAfford(BitRestorer::resourceCost));
	memorySwapperButton->Enable(CanAfford(MemorySwapper::resourceCost));
	corrupterButton->Enable(CanAfford(Corrupter::resourceCost));
	platformButton->Enable(CanAfford(MemoryAllocator::resourceCost));
	unitCreationList.Update(this);
	if(GetMouse(0).bPressed){
		if(NotClickingOnMinimap()){
			for(auto&u:units){
				if(u->ClickHandled(game,player_resources,units,IMAGES)){
					goto skipLeftClick; //Break out early because this instance reported it handled a click for us.
				}
			}
			if(UnitCreationClickHandled()){
				goto skipLeftClick;
			}
			for(auto&u:units){
				u->Deselect();
			}
			if(startingDragPos==CONSTANT::UNSELECTED){
				startingDragPos=GetWorldMousePos();
			}
		}
	}
	skipLeftClick:
	if(GetMouse(0).bReleased&&startingDragPos!=CONSTANT::UNSELECTED){
		vf2d endDragPos=GetWorldMousePos();
		if(endDragPos.x<startingDragPos.x){std::swap(startingDragPos.x,endDragPos.x);}
		if(endDragPos.y<startingDragPos.y){std::swap(startingDragPos.y,endDragPos.y);}
		geom2d::rect<float> selectionRegion(startingDragPos,endDragPos-startingDragPos);
		for(auto&u:units){
			if(u->IsFriendly()){
				if(geom2d::overlaps(selectionRegion,geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2))){
					u->Select();
				}
			}
		}
		startingDragPos=CONSTANT::UNSELECTED;
	}
}

vf2d VirusAttack::GetWorldMousePos(){	
	return game.ScreenToWorld(GetMousePos());
}

void VirusAttack::DrawSelectionRectangle(){
	if(startingDragPos!=CONSTANT::UNSELECTED){
		game.FillRectDecal(startingDragPos,GetWorldMousePos()-startingDragPos,{255,255,0,128});
	}
}

void VirusAttack::HandleRightClickMove(){
	if (GetMouse(1).bHeld){
		bool selectedTarget=false;
		for(auto&u:units){
			if(geom2d::overlaps(geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2),GetWorldMousePos())){
				for(auto&u2:units){
					if(&u!=&u2){
						if(!u->IsFriendly()&&u2->IsFriendly()&&u2->IsSelected()&&u2->CanInteractWithEnemies()){
							u2->SetTargetUnit(u);
						} else
						if(u->IsFriendly()&&u2->IsFriendly()&&u2->IsSelected()&&u2->CanInteractWithAllies()){
							u2->SetTargetUnit(u);
						}
					}
				}
				selectedTarget=true;
				break;
			}
		}
		if(!selectedTarget){
			for(auto&u:units){
				if(u->IsFriendly()&&u->IsSelected()&&u->CanMove()){
					//First see if we can attach to a collection point.
					for(auto&cp:collectionPoints){
						geom2d::rect<float>cpRect=geom2d::rect<float>({cp->pos-cp->img.Sprite()->Size()/2,cp->img.Sprite()->Size()});
						if(geom2d::overlaps(cpRect,GetWorldMousePos())){
							if(cp->attachedUnit.expired()){
								u->SetTargetCollectionPoint(cp,u);
								goto targetFound; //We found a target, so now we can just leave.
							}
						}
					}

					//Okay, nothing to do here. Simply move to the selected position.
					u->SetTargetLocation(GetWorldMousePos());
				}
			}
		}
		targetFound:
		int a;
	}
}

void VirusAttack::CollisionChecking(std::shared_ptr<Unit>u,std::shared_ptr<Unit>u2){
	if(u!=u2&&geom2d::overlaps(geom2d::circle<float>(u->GetPos(),u->GetUnitSize().x/2),geom2d::circle<float>(u2->GetPos(),u2->GetUnitSize().x/2))){
		geom2d::line<float>collisionLine(u->GetPos(),u2->GetPos());
		float maxDist=u->GetUnitSize().x/2+u2->GetUnitSize().x/2;
		float dist=maxDist-collisionLine.length();
		vf2d dir=collisionLine.vector().norm();
		if(u->IsMoveable()){
			u->SetPos(u->GetPos()-dir*dist/2);
		}
		if(u2->IsMoveable()){
			u2->SetPos(u2->GetPos()+dir*(dist+0.001)/2);
		}
	}
}

void VirusAttack::IdentifyClosestTarget(std::weak_ptr<Unit>&closestUnit,float&closestDist,std::shared_ptr<Unit>u,std::shared_ptr<Unit>u2){
	if(u==u2)return;
	bool canInteract;
	canInteract=
		(u->IsFriendly()&&u->CanInteractWithEnemies()&&!u2->IsFriendly())||
		(u->IsFriendly()&&u->CanInteractWithAllies()&&u2->IsFriendly()&&u->AutoAcquiresFriendlyTargets())||
		(!u->IsFriendly()&&u->CanInteractWithEnemies()&&u2->IsFriendly())||
		(!u->IsFriendly()&&u->CanInteractWithAllies()&&!u2->IsFriendly()&&u->AutoAcquiresFriendlyTargets());
	if(canInteract){
		geom2d::line<float>unitLine(u->GetPos(),u2->GetPos());
		if(unitLine.length()<closestDist){
			closestUnit=u2;
			closestDist=unitLine.length();
		}
	}
}

void VirusAttack::DrawMinimap(){
	DrawDecal(GetScreenSize()-IMAGES[MINIMAP_HUD]->Sprite()->Size(),IMAGES[MINIMAP_HUD]->Decal(),{1,1});
	vf2d minimapTL=GetScreenSize()-vf2d{64,64};
	vi2d worldPixelSize=CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE;
	vf2d viewingTilesPct=vf2d{float(ScreenWidth()),float(ScreenHeight())}/CONSTANT::TILE_SIZE/CONSTANT::WORLD_SIZE;
	SetDrawTarget(IMAGES[MINIMAP_OUTLINE]->Sprite());
	Clear(BLANK);
	DrawRect((game.GetWorldOffset()/worldPixelSize*64),viewingTilesPct*64/game.GetWorldScale());
	for(auto&u:units){
		vf2d squareSize=u->GetUnitSize()/vf2d(CONSTANT::WORLD_SIZE);
		squareSize.x=std::round(std::max(1.f,squareSize.x));
		squareSize.y=std::round(std::max(1.f,squareSize.y));
		FillRect(u->GetGhostPos()/worldPixelSize*64-squareSize,squareSize*2,u->IsFriendly()?GREEN:RED);
	}
	IMAGES[MINIMAP_OUTLINE]->Decal()->Update();
	SetDrawTarget(nullptr);
	DrawDecal(minimapTL,IMAGES[MINIMAP_OUTLINE]->Decal());
}

void VirusAttack::HandlePanAndZoom(float fElapsedTime){
	float speedScale=std::min(1.f,game.GetWorldScale().x);
	if(GetKey(A).bHeld/*||GetMouseX()<=CONSTANT::SCROLL_BOUNDARY*/){
		game.MoveWorldOffset(vf2d{-256*fElapsedTime,0}/speedScale);
	}
	if(GetKey(W).bHeld/*||GetMouseY()<=CONSTANT::SCROLL_BOUNDARY*/){
		game.MoveWorldOffset(vf2d{0,-256*fElapsedTime}/speedScale);
	}
	if(GetKey(S).bHeld/*||GetMouseY()>=ScreenHeight()-CONSTANT::SCROLL_BOUNDARY*/){
		game.MoveWorldOffset(vf2d{0,256*fElapsedTime}/speedScale);
	}
	if(GetKey(D).bHeld/*||GetMouseX()>=ScreenWidth()-CONSTANT::SCROLL_BOUNDARY*/){
		game.MoveWorldOffset(vf2d{256*fElapsedTime,0}/speedScale);
	}

	if(GetMouseWheel()>0){
		if(game.GetWorldScale().x<2){
			game.ZoomAtScreenPos(1.25,GetMousePos());
		}
	} else
	if(GetMouseWheel()<0){
		if(game.GetWorldScale().x>0.5){
			game.ZoomAtScreenPos(0.75,GetMousePos());
		}
	}
}

void VirusAttack::HandleMinimapClick(){
	if(startingDragPos==CONSTANT::UNSELECTED&&GetMouse(0).bHeld&&GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64){
		vf2d minimapTL=GetScreenSize()-vf2d{64,64};
		game.SetWorldOffset(vf2d(GetMousePos()-minimapTL)/64*CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE-vf2d(GetScreenSize())/game.GetWorldScale()/2.f);
	}
}

void VirusAttack::UpdateMatrixTexture(float fElapsedTime){
	if(matrixTimer==0){
		activeLetters.emplace_back(vf2d{float(rand()%64),float(64)},util::random(-40)-20,matrixLetters[rand()%matrixLetters.size()]);
		matrixTimer=util::random(0.125);
	}
	if(updatePixelsTimer==0){
		SetDrawTarget(IMAGES[MATRIX]->Sprite());
		Sprite*img=IMAGES[MATRIX]->Sprite();
		for(int y=63;y>=0;y--){
			for(int x=63;x>=0;x--){
				Pixel col=img->GetPixel(x,y);
				if(col.r>0){
					if(x>0){
						Pixel leftCol=img->GetPixel(x-1,y);
						if(leftCol.r<col.r){
							leftCol=PixelLerp(col,leftCol,0.125);
						}
						Draw(x-1,y,leftCol);
					}
					if(x<img->width-1){
						Pixel rightCol=img->GetPixel(x+1,y);
						if(rightCol.r<col.r){
							rightCol=PixelLerp(col,rightCol,0.125);
						}
						Draw(x+1,y,rightCol);
					}
					col/=8;
					Draw(x,y,col);
				}
			}
		}
		for(int y=0;y<64;y++){
			Draw({0,y},img->GetPixel(1,y));
		}
		SetDrawTarget(nullptr);
		updatePixelsTimer=0.1;
	}
	if(activeLetters.size()>0){
		SetDrawTarget(IMAGES[MATRIX]->Sprite());
		for(Letter&letter:activeLetters){
			letter.pos.y+=letter.spd*fElapsedTime;
			DrawString(letter.pos,std::string(1,letter.c));
		}
		SetDrawTarget(nullptr);
		IMAGES[MATRIX]->Decal()->Update();
	}
	matrixTimer=std::max(0.f,matrixTimer-fElapsedTime);
	updatePixelsTimer=std::max(0.f,updatePixelsTimer-fElapsedTime);
	std::erase_if(activeLetters,[](Letter&letter){return letter.pos.y<-32;});
}

void VirusAttack::RenderCollectionPoints(CollectionPoint*cp){
	geom2d::rect<float>cpRect=geom2d::rect<float>({cp->pos-cp->img.Sprite()->Size()/2,cp->img.Sprite()->Size()});
	geom2d::rect<float>viewRegion=geom2d::rect<float>({game.GetWorldTL(),game.GetWorldVisibleArea()});
	if(geom2d::overlaps(cpRect,viewRegion)){
		Pixel col;
		switch(cp->type){
			case HEALTH:{
				col=CONSTANT::HEALTH_COLOR;
			}break;
			case RANGE:{
				col=CONSTANT::RANGE_COLOR;
			}break;
			case ATKSPD:{
				col=CONSTANT::ATKSPD_COLOR;
			}break;
			case MOVESPD:{
				col=CONSTANT::MOVESPD_COLOR;
			}break;
			case PROCEDURE:{
				col=CONSTANT::PROCEDURE_COLOR;
			}break;
		}
		game.DrawRotatedDecal(cp->pos,cp->img.Decal(),cp->rot,cp->img.Sprite()->Size()/2,{1,1},col);
		if(geom2d::overlaps(cpRect,GetWorldMousePos())){
			game.DrawRotatedDecal(cp->pos,IMAGES[MEMORY_COLLECTION_POINT_HIGHLIGHT]->Decal(),cp->rot,cp->img.Sprite()->Size()/2,{1,1},col);
		}
	}
}

bool VirusAttack::OnUserUpdate(float fElapsedTime){
	UpdateMatrixTexture(fElapsedTime);
	HandleDraggingSelection();
	HandleRightClickMove();
	HandlePanAndZoom(fElapsedTime);
	HandleMinimapClick();
	AL.vecPos=game.ScreenToWorld(GetScreenSize()/2);
	AL.fSoundFXVolume=std::min(1.f,game.GetWorldScale().x);
	AL.OnUserUpdate(fElapsedTime);

	for(auto&tile:TileManager::visibleTiles){
		tile.second-=fElapsedTime;
	}

	std::erase_if(TileManager::visibleTiles,[](std::pair<vf2d,float> key){return key.second<=0;});

	for(auto&u:units){
		std::weak_ptr<Unit>closestUnit;
		float closestDist=999999;
		for(auto&u2:units){
			IdentifyClosestTarget(closestUnit,closestDist,u,u2);
			CollisionChecking(u,u2);
		}
		if(u->IsFriendly()){
			for(int y=-2;y<3;y++){
				for(int x=-2;x<3;x++){
					if(abs(x)+abs(y)<=2){
						TileManager::visibleTiles[u->GetPos()/24/4+vi2d(x,y)]=5;
					}
				}
			}
		}
		u->AttemptAttack(u,closestUnit,units,debuffIcons,IMAGES);
		u->_Update(this,SOUNDS,player_resources,enemy_resources);
	}

	std::erase_if(units,[&](std::shared_ptr<Unit>u){
		if(u->GetHealth()==0){
			u->OnDeath(SOUNDS);
			deathAnimations.emplace_back(std::make_unique<DeathAnimation>(this,u->GetPos(),u->GetImage(),*IMAGES[MATRIX],u->IsFriendly()));
			return true;
		} else {
			return false;
		}
	});


	game.DrawPartialDecal({0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,IMAGES[TILE]->Decal(),{0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,DARK_GREEN);

	for(auto&u:units){
		u->DrawRangeIndicator(this,game,IMAGES);
	}

	for(auto&u:units){
		u->Draw(game,IMAGES);
	}
	for(auto&deadUnit:deathAnimations){
		deadUnit->Update(fElapsedTime);
		deadUnit->Draw(game,this);
	}

	std::erase_if(deathAnimations,[](auto&u){return u->IsDone();});

	for(auto&collectionPoint:collectionPoints){
		collectionPoint->Update(this,*IMAGES[MATRIX]);
		RenderCollectionPoints(collectionPoint.get());
	}

	for(auto&u:units){
		u->DrawUnitDamageStats(this,game,IMAGES);
	}

	for(DebuffIcon&icon:debuffIcons){
		icon.Update(fElapsedTime);
		icon.Draw(game);
	}

	std::erase_if(debuffIcons,[](DebuffIcon&icon){return icon.lifetime<=0;});

	for(auto&u:units){
		u->_DrawHud(game,IMAGES);
	}

	DrawSelectionRectangle();
	RenderFogOfWar();

	unitCreationList.DrawDecal(this);

	DrawResourceBar();

	DrawMinimap();

	std::sort(units.begin(),units.end(),[&](auto&u1,auto&u2){
		float dist1=geom2d::line<float>(u1->GetGhostPos(),GetWorldMousePos()).length();
		float dist2=geom2d::line<float>(u2->GetGhostPos(),GetWorldMousePos()).length();
		return dist1>dist2;});

	return true;
}

void VirusAttack::DrawResourceBar(){
	GradientFillRectDecal({0,0},{float(ScreenWidth()),12.f},BLACK,{VERY_DARK_BLUE.r,VERY_DARK_BLUE.g,VERY_DARK_BLUE.b,128},{VERY_DARK_BLUE.r,VERY_DARK_BLUE.g,VERY_DARK_BLUE.b,128},BLACK);
	DrawRectDecal({0,0},{float(ScreenWidth()),12.f},{3, 194, 252});
	auto DrawResourceDisplay=[&](int index,int resourceValue,Pixel col){
		DrawDecal({6.f+index*42,1.f},IMAGES[RESOURCE]->Decal(),{1,1},col);
		DrawShadowStringDecal({6.f+index*42+20,2.f},std::to_string(resourceValue),col,BLACK);
	};
	DrawResourceDisplay(0,player_resources.health,CONSTANT::HEALTH_COLOR);
	DrawResourceDisplay(1,player_resources.atkSpd,CONSTANT::ATKSPD_COLOR);
	DrawResourceDisplay(2,player_resources.moveSpd,CONSTANT::MOVESPD_COLOR);
	DrawResourceDisplay(3,player_resources.range,CONSTANT::RANGE_COLOR);
	DrawResourceDisplay(4,player_resources.procedure,CONSTANT::PROCEDURE_COLOR);
}

void VirusAttack::RenderFogOfWar(){
	for(int y=game.GetTopLeftTile().y/96-1;y<=game.GetBottomRightTile().y/96+1;y++){
		for(int x=game.GetTopLeftTile().x/96-1;x<=game.GetBottomRightTile().x/96+1;x++){
			if(TileManager::visibleTiles.count(vi2d{x,y})==0){
				if(x>=0&&y>=0&&x<=CONSTANT::WORLD_SIZE.x*CONSTANT::TILE_SIZE.x&&y<=CONSTANT::WORLD_SIZE.y*CONSTANT::TILE_SIZE.y){
					game.FillRectDecal(vf2d{float(x),float(y)}*96,{96,96},{0,0,0,128});
				}
			}
		}
	}
}

bool VirusAttack::CanAfford(std::vector<Memory>&unitCosts){
	for(Memory&mem:unitCosts){
		switch(mem.type){
			case HEALTH:{
				if(player_resources.health<mem.size)return false;
			}break;
			case ATKSPD:{
				if(player_resources.atkSpd<mem.size)return false;
			}break;
			case MOVESPD:{
				if(player_resources.moveSpd<mem.size)return false;
			}break;
			case RANGE:{
				if(player_resources.range<mem.size)return false;
			}break;
			case PROCEDURE:{
				if(player_resources.procedure<mem.size)return false;
			}break;
		}
	}
	return true;
}

int main()
{
	VirusAttack app;
	if (app.Construct(426, 320, 4, 4))
		app.Start();
	return 0;
}