#include "client/render/WorldRenderer.h"
#include "client/render/loader/WorldLoader.h"
#include "client/render/Renderer.h"
#include "client/render/gui/imgui/imgui.h"
#include "gui/imgui/imgui_internal.h"
#include "client/window/Display.h"
#include "client/game/ClientGame.h"
#include "client/Client.h"
#include "td/misc/Format.h"

namespace td {
namespace render {

void WorldRenderer::LoadModels() {
	utils::LOGD("World Created !");
	m_WorldVao = std::make_unique<GL::VertexArray>(std::move(WorldLoader::LoadWorldModel(m_World)));
	m_MobVao = std::make_unique<GL::VertexArray>(std::move(WorldLoader::LoadMobModel()));
	m_SelectTileVao = std::make_unique<GL::VertexArray>(std::move(WorldLoader::LoadTileSelectModel()));
	utils::LOGD(utils::format("Vertex Count : %u", m_WorldVao->GetVertexCount()));
}

WorldRenderer::WorldRenderer(game::World* world, client::ClientGame* client) : m_Client(client), m_Renderer(m_Client->GetRenderer()), m_World(world), m_Zoom(0.1) {
	m_TowerPlacePopup = std::make_unique<gui::TowerPlacePopup>(m_Client->GetClient());
	m_TowerUpgradePopup = std::make_unique<gui::TowerUpgradePopup>(m_Client->GetClient());
	m_MobTooltip = std::make_unique<gui::MobTooltip>(m_Client->GetClient());
	m_CastleTooltip = std::make_unique<gui::CastleTooltip>(m_Client->GetClient());
	m_Client->GetWorldClient().GetWorldNotifier().BindListener(this);
}

void WorldRenderer::UpdateCursorPos() {
	m_CursorPos = GetCursorWorldPos();
}

void WorldRenderer::Update() {
	if (m_WorldVao == nullptr)
		return;

	ImGuiIO& io = ImGui::GetIO();

	if (io.MouseDown[0] && !ImGui::IsAnyItemActive() && !ImGui::IsAnyItemHovered()) {
		ImVec2 mousePos = ImGui::GetIO().MousePos;
		ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
		m_Renderer->SetCamMovement({mousePos.x - mouseDelta.x, mousePos.y - mouseDelta.y}, { mousePos.x, mousePos.y });
	}
	
	if (io.MouseDown[1] && !ImGui::IsAnyItemActive() && !ImGui::IsAnyItemHovered()) {
		ImVec2 mouseDelta = ImGui::GetIO().MouseDelta;
		m_Renderer->SetCamAngularMovement({ mouseDelta.x, mouseDelta.y });
	}

	if (io.MouseWheel != 0) {
		ChangeZoom(io.MouseWheel);
	}

	UpdateCursorPos();

	if (ImGui::IsMouseClicked(0)) {
		if (!m_TowerUpgradePopup->IsPopupOpened()) {
			m_HoldCursorPos = { io.MousePos.x, io.MousePos.y };
		}
	}

	if (ImGui::IsMouseDoubleClicked(1)) RemoveTower();
}

void WorldRenderer::RemoveTower() {
	Vec2f cursorPos = GetCursorWorldPos();

	game::TowerPtr clickedTower = m_World->GetTower(cursorPos);

	if (clickedTower != nullptr) {
		m_Client->GetClient()->RemoveTower(clickedTower->GetID());
	}
}

void WorldRenderer::RenderWorld() const {
	m_Renderer->RenderVAO(*m_WorldVao);
}

void WorldRenderer::RenderMobs() const {
	for (game::MobPtr mob : m_World->GetMobList()) {
		Renderer::Model model;
		model.vao = m_MobVao.get();
		model.positon = { mob->GetCenterX(), 0, mob->GetCenterY() };
		model.color = mob->HasTakenDamage() ? Vec3f{ 1, 0.5, 0.5 } : Vec3f{ 1, 1, 1 };
		m_Renderer->RenderModel(model);
	}
}

void WorldRenderer::RenderTowers() const {
	if (!m_TowersCache.IsEmpty())
		m_Renderer->RenderVAO(m_TowersCache.GetVertexArray());
}

void WorldRenderer::RenderTileSelect() const {
	if (ImGui::IsAnyItemHovered()) return;

	if (m_MobTooltip->IsShown() || m_CastleTooltip->IsShown()) return;

	Renderer::Model tileSelectModel;
	tileSelectModel.vao = m_SelectTileVao.get();
	tileSelectModel.positon = { std::floor(m_CursorPos.x), 0, std::floor(m_CursorPos.y) };

	m_Renderer->RenderModel(tileSelectModel);
}

void WorldRenderer::RenderPopups() {
	m_TowerPlacePopup->Render();
	m_TowerUpgradePopup->Render();
}

void WorldRenderer::Render() {
	if (m_WorldVao == nullptr)
		return;
	RenderWorld();
	RenderMobs();
	RenderTowers();
	RenderTileSelect();
	RenderTooltips();
	RenderPopups();
	DetectClick();
}

WorldRenderer::~WorldRenderer() {

}

void WorldRenderer::RenderTooltips() const {
	RenderMobTooltip();
	RenderCastleTooltip();
}

void WorldRenderer::ResetCam() {
	m_Renderer->ResetCamLook();
}

void WorldRenderer::MoveCam(float relativeX, float relativeY) {
	if (m_WorldVao == nullptr)
		return;
	
}

void WorldRenderer::ChangeZoom(float zoomStep) {
	if (m_WorldVao == nullptr)
		return;

	m_Renderer->AddZoom(zoomStep);
}

void WorldRenderer::Click() {
	const game::TowerPtr tower = m_Client->GetWorld().GetTower(GetClickWorldPos());
	m_TowerPlacePopup->SetClickPos(GetClickWorldPos());
	m_TowerUpgradePopup->SetClickPos(GetClickWorldPos());
	if (tower != nullptr) { // there is a tower here
		ImGui::OpenPopup("TowerUpgrade");
	} else if (m_Client->GetWorld().CanPlaceLittleTower(GetClickWorldPos(), m_Client->GetPlayer()->GetID())) {
		ImGui::OpenPopup("TowerPlace");
	}
}

void WorldRenderer::SetCamPos(float camX, float camY) {
	m_CamPos = { camX, camY };
	m_Renderer->SetCamLook({ camX, camY });
}

void WorldRenderer::DetectClick() {
	ImGuiIO& io = ImGui::GetIO();
	if (ImGui::IsMouseReleased(0) && !ImGui::IsAnyItemHovered() && !ImGui::IsAnyItemFocused()) {
		Vec2f cursorPos = { io.MousePos.x, io.MousePos.y };
		if (cursorPos == m_HoldCursorPos) {
			m_LastClicked = m_HoldCursorPos;
			Click();
		}
	}
}

void WorldRenderer::RenderMobTooltip() const {
	if (ImGui::IsAnyItemHovered()) return;

	DetectMobHovering();
	m_MobTooltip->Render();
}

void WorldRenderer::RenderCastleTooltip() const {
	if (ImGui::IsAnyItemHovered()) return;

	DetectCastleHovering();
	m_CastleTooltip->Render();
}

void WorldRenderer::DetectMobHovering() const {
	Vec2f cursorWorldPos = GetCursorWorldPos();
	for (game::MobPtr mob : m_World->GetMobList()) {
		if (mob->CollidesWith({ cursorWorldPos.x, cursorWorldPos.y })) {
			m_MobTooltip->SetMob(mob.get());
			return;
		}
	}
	m_MobTooltip->SetMob(nullptr);
}

void WorldRenderer::DetectCastleHovering() const {
	Vec2f cursorWorldPos = GetCursorWorldPos();
	for (const game::Team& team : m_World->GetTeams()) {
		if (team.GetCastle().CollidesWith({ cursorWorldPos.x, cursorWorldPos.y })) {
			m_CastleTooltip->SetCastle(&team.GetCastle());
			return;
		}
	}
	m_CastleTooltip->SetCastle(nullptr);
}

void WorldRenderer::OnTowerAdd(game::TowerPtr tower) {
	WorldLoader::RenderData RenderData = WorldLoader::LoadTowerModel(tower);
	m_TowersCache.AddData(tower->GetID(), RenderData.positions, RenderData.colors);
	m_TowersCache.UpdateVertexArray();
}

void WorldRenderer::OnTowerRemove(game::TowerPtr tower) {
	m_TowersCache.RemoveData(tower->GetID());
	m_TowersCache.UpdateVertexArray();
}

Vec2f WorldRenderer::GetCursorWorldPos() const {
	ImGuiIO& io = ImGui::GetIO();
	return m_Renderer->GetCursorWorldPos({ io.MousePos.x, io.MousePos.y }, Display::GetWindowWidth(), Display::GetWindowHeight());
}

Vec2f WorldRenderer::GetClickWorldPos() const {
	return m_Renderer->GetCursorWorldPos(m_LastClicked, Display::GetWindowWidth(), Display::GetWindowHeight());
}

} // namespace render
} // namespace td
