/********************************************************************
	Minecraft: Pocket Edition - Decompilation Project
	Copyright (C) 2023 iProgramInCpp
	
	The following code is licensed under the BSD 1 clause license.
	SPDX-License-Identifier: BSD-1-Clause
 ********************************************************************/

#include "client/app/Minecraft.hpp"
#include "client/gui/screens/IngameBlockSelectionScreen.hpp"
#include "client/gui/screens/ChatScreen.hpp"
#include "client/renderer/entity/ItemRenderer.hpp"

#ifdef _WIN32
#pragma warning(disable : 4244)
#endif

#ifdef ENH_USE_GUI_SCALE_2
float Gui::InvGuiScale = 1.0f / 2.0f;
#else
float Gui::InvGuiScale = 1.0f / 3.0f;
#endif

Gui::Gui(Minecraft* pMinecraft)
{
	field_8 = 0;
	field_C = "";
	field_24 = 0;
	field_28 = 0;
	field_2C = 0;
	field_9FC = 0;
	field_A00 = "";
	field_A18 = 0;
	field_A1C = false;
	field_A20 = 1.0f;
	field_A3C = true;
	m_bRenderMessages = true;

	m_pMinecraft = pMinecraft;

	xglGenBuffers(1, &m_renderChunk.field_0);
}

void Gui::addMessage(const std::string& s)
{
	// if the message contains a new line, add each line separately:
	if (s.find("\n") != std::string::npos)
	{
		std::stringstream ss(s);
		std::string line;
		while (std::getline(ss, line))
			addMessage(line);

		return;
	}

	std::string str = s;

	while (m_pMinecraft->m_pFont->width(str) > 320)
	{
		int i = 2;
		for (; i < int(str.size()); i++)
		{
			std::string sstr = str.substr(0, i);

			// this sucks
			if (m_pMinecraft->m_pFont->width(sstr) > 320)
				break;
		}

		std::string sstr = str.substr(0, i - 1);
		addMessage(sstr);
		str = str.substr(i - 1);
	}

	if (m_guiMessages.size() > 50)
	{
		m_guiMessages.erase(m_guiMessages.end());
	}

	m_guiMessages.insert(m_guiMessages.begin(), GuiMessage(str, 0));
}

void Gui::setNowPlaying(const std::string& str)
{
	field_A00 = "Now playing: " + str;
	field_A18 = 60;
	field_A1C = true;
}

void Gui::renderVignette(float a2, int a3, int a4)
{
	a2 = 1.0f - a2;
	if (a2 > 1.0f)
		a2 = 1.0f;
	if (a2 < 0.0f)
		a2 = 0.0f;

	field_A20 += ((a2 - field_A20) * 0.01f);
	glDisable(GL_DEPTH_TEST);
	glDepthMask(false);
	glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR);
	glColor4f(field_A20, field_A20, field_A20, 1.0f);

	//! @BUG: No misc/vignette.png to be found in the original.
	//! This function is unused anyways
	m_pMinecraft->m_pTextures->loadAndBindTexture("misc/vignette.png");

	Tesselator& t = Tesselator::instance;
	t.begin();
	t.vertexUV(0.0f, a4,   -90.0f, 0.0f, 1.0f);
	t.vertexUV(a3,   a4,   -90.0f, 1.0f, 1.0f);
	t.vertexUV(a3,   0.0f, -90.0f, 1.0f, 0.0f);
	t.vertexUV(0.0f, 0.0f, -90.0f, 0.0f, 0.0f);
	t.draw();

	glDepthMask(true);
	glEnable(GL_DEPTH_TEST);
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

void Gui::inventoryUpdated()
{
	field_A3C = true;
}

void Gui::render(float f, bool bHaveScreen, int mouseX, int mouseY)
{
	Minecraft* m = m_pMinecraft;

	m->m_pGameRenderer->setupGuiScreen();

	if (!m->m_pLevel || !m->m_pLocalPlayer)
		return;

	bool isTouchscreen = m->isTouchscreen();

	field_4 = -90.0f;

#ifndef ENH_TRANSPARENT_HOTBAR
	glColor4f(1.0f, 1.0f, 1.0f, 0.5f);
#else
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
#endif

	m->m_pTextures->loadAndBindTexture("gui/gui.png");

	Inventory* pInventory = m->m_pLocalPlayer->m_pInventory;

	field_4 = -90.0f;

	int width  = Minecraft::width  * InvGuiScale,
		height = Minecraft::height * InvGuiScale;

#ifdef ENH_TRANSPARENT_HOTBAR
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
#endif

	int nSlots = getNumSlots();
	int hotbarWidth = 2 + nSlots * 20;

	// hotbar
	int cenX = width / 2;
	blit(cenX - hotbarWidth / 2, height - 22, 0, 0, hotbarWidth, 22, 0, 0);

	// selection mark
	blit(cenX - 1 - hotbarWidth / 2 + 20 * pInventory->m_SelectedHotbarSlot, height - 23, 0, 22, 24, 22, 0, 0);

	m->m_pTextures->loadAndBindTexture("gui/icons.png");

	if (m->useSplitControls())
	{
#ifndef ENH_TRANSPARENT_HOTBAR
		glEnable(GL_BLEND);
#endif

		// draw crosshair
		glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
		blit(cenX - 8, height / 2 - 8, 0, 0, 16, 16, 0, 0);

#ifndef ENH_TRANSPARENT_HOTBAR
		glDisable(GL_BLEND);
#endif
	}
	else
	{
		// if needed, draw feedback
		
		// NOTE: real Minecraft PE takes it directly from the gamemode as "current progress" and
		// "last progress". Well guess what? The game mode in question updates our field_8 with
		// the pre-interpolated break progress! Isn't that awesome?!
		float breakProgress = field_8;

		// don't know about this if-structure, it feels like it'd be like
		// if (field_C >= 0.0f && breakProgress <= 0.0f)
		//     that;
		// else
		//     this;
		if (breakProgress > 0.0f || m_pMinecraft->m_pInputHolder->m_feedbackAlpha < 0.0f)
		{
			if (breakProgress > 0.0f)
			{
				float xPos = m_pMinecraft->m_pInputHolder->m_feedbackX;
				float yPos = m_pMinecraft->m_pInputHolder->m_feedbackY;

				m_pMinecraft->m_pTextures->loadAndBindTexture("gui/feedback_outer.png");
				glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
				glEnable(GL_BLEND);
				glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
				blit(InvGuiScale * xPos - 44.0f, InvGuiScale * yPos - 44.0f, 0, 0, 88, 88, 256, 256);

				glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
				m_pMinecraft->m_pTextures->loadAndBindTexture("gui/feedback_fill.png");

				// note: scale starts from 4.0f
				float halfWidth = (40.0f * breakProgress + 48.0f) / 2.0f;

				blit(InvGuiScale * xPos - halfWidth, InvGuiScale * yPos - halfWidth, 0, 0, halfWidth * 2, halfWidth * 2, 256, 256);

				glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
				glDisable(GL_BLEND);
			}
		}
		else
		{
			float xPos = m_pMinecraft->m_pInputHolder->m_feedbackX;
			float yPos = m_pMinecraft->m_pInputHolder->m_feedbackY;

			m_pMinecraft->m_pTextures->loadAndBindTexture("gui/feedback_outer.png");
			glColor4f(1.0f, 1.0f, 1.0f, Mth::Min(1.0f, m_pMinecraft->m_pInputHolder->m_feedbackAlpha));
			glEnable(GL_BLEND);
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
			blit(InvGuiScale * xPos - 44.0f, InvGuiScale * yPos - 44.0f, 0, 0, 88, 88, 256, 256);
			glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
			glDisable(GL_BLEND);
		}
	}

#ifdef ENH_TRANSPARENT_HOTBAR
	glDisable(GL_BLEND);
#endif

	if (m_pMinecraft->m_pGameMode->canHurtPlayer())
	{
		LocalPlayer* pLP = m_pMinecraft->m_pLocalPlayer;

		// why??
		m_random.init_genrand(312871 * field_9FC);

		int emptyHeartX = 16;
		bool b1 = false;
		if (pLP->field_B8 < 10)
		{
			b1 = pLP->field_B8 / 3 % 2;
			emptyHeartX += 9 * b1;
		}

		// @NOTE: At the default scale, this would go off screen.

		int heartX = cenX - 191; // why?
		int heartYStart = height - 10;

		//@NOTE: Alpha-style health UI. I'll probably remove this on release.
#ifndef ORIGINAL_CODE
		heartX = cenX - 91;
		heartYStart = height - 32;
#endif

		int playerHealth = pLP->m_health;

		for (int healthNo = 1; healthNo <= C_MAX_MOB_HEALTH; healthNo += 2)
		{
			int heartY = heartYStart;

			if (playerHealth <= 4 && m_random.genrand_int32() % 2)
				heartY++;

			blit(heartX, heartY, emptyHeartX, 0, 9, 9, 0, 0);

			if (b1)
			{
				if (healthNo < pLP->field_100)
					blit(heartX, heartY, 70, 0, 9, 9, 0, 0);
				else if (healthNo == pLP->field_100)
					blit(heartX, heartY, 79, 0, 9, 9, 0, 0);
			}

			if (healthNo < playerHealth)
				blit(heartX, heartY, 52, 0, 9, 9, 0, 0);
			else if (healthNo == playerHealth)
				blit(heartX, heartY, 61, 0, 9, 9, 0, 0);

			heartX += 8;
		}

		if (m->m_pLocalPlayer->isUnderLiquid(Material::water))
		{
			int breathRaw = m->m_pLocalPlayer->field_BC;
			int breathFull  = int(ceilf((float(breathRaw - 2) * 10.0f) / 300.0f));
			int breathMeter = int(ceilf((float(breathRaw)     * 10.0f) / 300.0f)) - breathFull;

			int bubbleX = cenX - 191;
			int bubbleY = height - 19;

#ifndef ORIGINAL_CODE
			bubbleX = cenX - 91;
			bubbleY = height - 41;
#endif

			//@NOTE: Not sure this works as it should

			for (int bubbleNo = 0; bubbleNo < breathFull + breathMeter; bubbleNo++)
			{
				if (bubbleNo < breathFull)
					blit(bubbleX, bubbleY, 16, 18, 9, 9, 0, 0);
				else
					blit(bubbleX, bubbleY, 25, 18, 9, 9, 0, 0);

				bubbleX += 8;
			}
		}
	}

	m->m_pTextures->loadAndBindTexture("gui/gui_blocks.png");

	int diff = m->isTouchscreen();

	int slotX = cenX - hotbarWidth / 2 + 3;
	for (int i = 0; i < nSlots - diff; i++)
	{
		renderSlot(i, slotX, height - 19, f);

		slotX += 20;
	}
	
	slotX = cenX - hotbarWidth / 2 + 3;
	for (int i = 0; i < nSlots - diff; i++)
	{
		renderSlotOverlay(i, slotX, height - 19, f);

		slotX += 20;
	}

#undef DIFF

	field_A3C = false;

	// blit the "more items" button
	if (m->isTouchscreen())
	{
		m->m_pTextures->loadAndBindTexture(C_TERRAIN_NAME);
		blit(cenX + hotbarWidth / 2 - 19, height - 19, 208, 208, 16, 16, 0, 0);
	}

	// render messages
	if (m_bRenderMessages)
	{
		renderMessages(false);
	}
}

void Gui::tick()
{
	if (field_A18 > 0)
		field_A18--;

	field_9FC++;

	for (int i = 0; i < int(m_guiMessages.size()); i++)
	{
		GuiMessage& msg = m_guiMessages[i];
		msg.field_18++;
	}
}

void Gui::renderSlot(int slot, int x, int y, float f)
{
	Inventory* pInv = m_pMinecraft->m_pLocalPlayer->m_pInventory;

	ItemInstance* pInst = pInv->getQuickSlotItem(slot);
	if (!pInst)
		return;

	if (!pInst->m_itemID)
		return;

	ItemRenderer::renderGuiItem(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, pInst, x, y, true);
}

void Gui::renderSlotOverlay(int slot, int x, int y, float f)
{
	Inventory* pInv = m_pMinecraft->m_pLocalPlayer->m_pInventory;

	ItemInstance* pInst = pInv->getQuickSlotItem(slot);
	if (!pInst)
		return;

	if (!pInst->m_itemID)
		return;

	ItemRenderer::renderGuiItemOverlay(m_pMinecraft->m_pFont, m_pMinecraft->m_pTextures, pInst, x, y);
}

int Gui::getSlotIdAt(int mouseX, int mouseY)
{
	int scaledY = int(InvGuiScale * mouseY);
	int scaledHeight = int(InvGuiScale * Minecraft::height);

	if (scaledY >= scaledHeight)
		return -1;
	if (scaledY < scaledHeight - 19)
		return -1;

	int hotbarOffset = getNumSlots() * 20 / 2 - 2;

	int slotX = (int(InvGuiScale * mouseX) - int(InvGuiScale * Minecraft::width) / 2 + hotbarOffset + 20) / 20;

	if (slotX >= 0)
		slotX--;

	if (slotX >= getNumSlots())
		slotX = -1;

	return slotX;
}

bool Gui::isInside(int mouseX, int mouseY)
{
	return getSlotIdAt(mouseX, mouseY) != -1;
}

void Gui::handleClick(int clickID, int mouseX, int mouseY)
{
	if (clickID != 1)
		return;

	int slot = getSlotIdAt(mouseX, mouseY);
	if (slot == -1)
		return;

	if (m_pMinecraft->isTouchscreen() && slot == getNumSlots() - 1)
		m_pMinecraft->setScreen(new IngameBlockSelectionScreen);
	else
		m_pMinecraft->m_pLocalPlayer->m_pInventory->selectSlot(slot);
}

void Gui::handleKeyPressed(int keyCode)
{
	if (m_pMinecraft->getOptions()->isKey(KM_INVENTORY, keyCode))
	{
		m_pMinecraft->setScreen(new IngameBlockSelectionScreen);
		return;
	}

	int maxItems = getNumSlots() - 1;
	if (m_pMinecraft->isTouchscreen())
		maxItems--;

	if (m_pMinecraft->getOptions()->isKey(KM_SLOT_R, keyCode))
	{
		int* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_SelectedHotbarSlot;

		if (*slot <= maxItems)
			(*slot)++;

		return;
	}
	if (m_pMinecraft->getOptions()->isKey(KM_SLOT_L, keyCode))
	{
		int* slot = &m_pMinecraft->m_pLocalPlayer->m_pInventory->m_SelectedHotbarSlot;

		if (*slot > 0)
			(*slot)--;

		return;
	}

	if (m_pMinecraft->getOptions()->isKey(KM_CHAT_CMD, keyCode) || m_pMinecraft->getOptions()->isKey(KM_CHAT, keyCode))
	{
		if (m_pMinecraft->m_pScreen)
			return;

		m_pMinecraft->setScreen(new ChatScreen(m_pMinecraft->getOptions()->isKey(KM_CHAT_CMD, keyCode)));
		return;
	}
}

void Gui::renderMessages(bool bShowAll)
{
	//int width = Minecraft::width * InvGuiScale,
    int height = Minecraft::height * InvGuiScale;

	int topEdge = height - 49;
	
	for (int i = 0; i < int(m_guiMessages.size()); i++)
	{
		GuiMessage& msg = m_guiMessages[i];
		if (!bShowAll && msg.field_18 > 199)
			continue;

		int bkgdColor = 0x7F000000, textColor = 0xFFFFFFFF;

		float fade = 1.0f;
		
		if (!bShowAll)
		{
			fade = 10.0f * (1.0f - (float(msg.field_18) / 200.0f));
			if (fade <= 0.0f)
				continue;

			if (fade < 1.0f)
			{
				int x = int(fade * fade * 255.0f);
				if (x == 0)
					continue;

				bkgdColor = (x / 2) << 24;
				textColor = (x << 24) + 0xFFFFFF;
			}
		}

		fill(2, topEdge, 322, topEdge + 9, bkgdColor);
		glEnable(GL_BLEND);
		m_pMinecraft->m_pFont->drawShadow(msg.msg, 2, topEdge + 1, textColor);

		topEdge -= 9;
	}

	glDisable(GL_BLEND);
}

int Gui::getNumSlots()
{
	if (m_pMinecraft->isTouchscreen())
		return 4;

	return 9;
}

int Gui::getNumUsableSlots()
{
	return getNumSlots() - m_pMinecraft->isTouchscreen();
}

RectangleArea Gui::getRectangleArea(bool b)
{
	float centerX = Minecraft::width / 2;
	float hotbarWidthHalf = (10 * getNumSlots() + 5) / InvGuiScale;
	return RectangleArea(
		b ? (centerX - hotbarWidthHalf) : 0,
		Minecraft::height - 24.0f / InvGuiScale,
		centerX + hotbarWidthHalf,
		Minecraft::height);
}
