#define OLC_PGE_APPLICATION
#define OLC_SOUNDWAVE
#define OLC_PGEX_TRANSFORMEDVIEW
#include "olcUTIL_Geometry2D.h"
#include "TileManager.h"

#include "VirusAttack.h"

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

bool VirusAttack::OnUserCreate(){
	game.Initialise(GetScreenSize());

	CONSTANT::VIRUS_IMG1.Load("assets/unit.png");
	CONSTANT::SELECTION_CIRCLE.Load("assets/selection_circle.png");

	TILE.Load("assets/tile.png",nullptr,false,false);
	MINIMAP_HUD.Load("assets/minimap_hud.png");
	OUTLINE.Load("assets/outline.png");
	MINIMAP_OUTLINE.Create(64,64);

	units.push_back(std::make_unique<BasicUnit>(vf2d{32,32},CONSTANT::VIRUS_IMG1,true));
	for(int i=0;i<10;i++){
		if(rand()%2==0){
			units.push_back(std::make_unique<BasicUnit>(vf2d{float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},CONSTANT::VIRUS_IMG1,true));
		} else {
			units.push_back(std::make_unique<BasicUnit2>(vf2d{float(rand()%ScreenWidth()),float(rand()%ScreenHeight())},CONSTANT::VIRUS_IMG1,false));
		}
	}
	return true;
}

void VirusAttack::HandleDraggingSelection(){
	auto NotClickingOnMinimap=[&](){return !(GetMouseX()>=ScreenWidth()-64&&GetMouseY()>=ScreenHeight()-64);};
	if(GetMouse(0).bPressed){
		if(NotClickingOnMinimap()){
			for(auto&u:units){
				u->Deselect();
			}
			if(startingDragPos==CONSTANT::UNSELECTED){
				startingDragPos=GetWorldMousePos();
			}
		}
	}
	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,u->GetPos())){
					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).bPressed){
		bool selectedTarget=false;
		for(auto&u:units){
			if(!u->IsFriendly()){
				geom2d::rect<float> unitRegion(u->GetPos()-u->GetUnitSize()/2,u->GetUnitSize());
				if(geom2d::overlaps(unitRegion,GetWorldMousePos())){
					for(auto&u2:units){
						if(u2->IsFriendly()&&u2->IsSelected()){
							u2->SetTargetUnit(u);
						}
					}
					selectedTarget=true;
					break;
				}
			}
		}
		if(!selectedTarget){
			for(auto&u:units){
				if(u->IsFriendly()&&u->IsSelected()){
					u->SetTargetLocation(GetWorldMousePos());
				}
			}
		}
	}
}

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();
		u->SetPos(u->GetPos()-dir*dist/2);
		u2->SetPos(u2->GetPos()+dir*dist/2);
	}
}

void VirusAttack::IdentifyClosestTarget(Unit*&closestUnit,float&closestDist,std::shared_ptr<Unit>u,std::shared_ptr<Unit>u2){
	if(u->IsFriendly()!=u2->IsFriendly()){
		geom2d::line<float>unitLine(u->GetPos(),u2->GetPos());
		if(unitLine.length()<closestDist){
			closestUnit=u2.get();
			closestDist=unitLine.length();
		}
	}
}

void VirusAttack::DrawMinimap(){
	DrawDecal(GetScreenSize()-MINIMAP_HUD.Sprite()->Size(),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(MINIMAP_OUTLINE.Sprite());
	for(int y=0;y<64;y++){
		for(int x=0;x<64;x++){
			Draw(x,y,BLANK);
		}
	}
	DrawRect((game.GetWorldOffset()/worldPixelSize*64),viewingTilesPct*64/game.GetWorldScale());
	for(auto&u:units){
		FillRect(u->GetGhostPos()/worldPixelSize*64,vf2d{2,2}*u->GetUnitSize()/24,u->IsFriendly()?GREEN:RED);
	}
	MINIMAP_OUTLINE.Decal()->Update();
	SetDrawTarget(nullptr);
	DrawDecal(minimapTL,MINIMAP_OUTLINE.Decal());
}

void VirusAttack::HandlePanAndZoom(float fElapsedTime){
	float speedScale=std::min(1.f,game.GetWorldScale().x);
	if(GetKey(A).bHeld){
		game.MoveWorldOffset(vf2d{-256*fElapsedTime,0}/speedScale);
	}
	if(GetKey(W).bHeld){
		game.MoveWorldOffset(vf2d{0,-256*fElapsedTime}/speedScale);
	}
	if(GetKey(S).bHeld){
		game.MoveWorldOffset(vf2d{0,256*fElapsedTime}/speedScale);
	}
	if(GetKey(D).bHeld){
		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);
	}
}

bool VirusAttack::OnUserUpdate(float fElapsedTime){
	HandleDraggingSelection();
	HandleRightClickMove();
	HandlePanAndZoom(fElapsedTime);
	HandleMinimapClick();

	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){
		Unit*closestUnit=nullptr;
		float closestDist=999999;
		for(auto&u2:units){
			IdentifyClosestTarget(closestUnit,closestDist,u,u2);
			CollisionChecking(u,u2);
		}
		if(u->IsFriendly()){
			for(int y=-1;y<2;y++){
				for(int x=-1;x<2;x++){
					TileManager::visibleTiles[u->GetPos()/24/4+vi2d(x,y)]=5;
				}
			}
		}
		u->AttemptAttack(closestUnit);
		u->Update(fElapsedTime);
	}
	for(auto&u:units){
		if(!u->GhostInFogOfWar()&&u->InFogOfWar()){
			u->HideGhost();
		}
	}
	game.DrawPartialDecal({0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,TILE.Decal(),{0,0},CONSTANT::WORLD_SIZE*CONSTANT::TILE_SIZE,DARK_GREEN);

	for(auto&u:units){
		u->Draw(game);
	}
	for(auto&u:units){
		u->DrawHud(game);
	}

	DrawSelectionRectangle();
	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});
				}
			}
		}
	}

	DrawMinimap();

	return true;
}

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