/********************************************************************
	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 "Tesselator.hpp"
#include "thirdparty/GL/GL.hpp"
#include "common/Utils.hpp"

#include <cstddef>

int dword_2514A4 = 0;

Tesselator Tesselator::instance;

Tesselator::Tesselator(int allotedSize)
{
	m_pVertices = nullptr;
	field_4 = 0;
	m_offsetX = 0.0f;
	m_offsetY = 0.0f;
	m_offsetZ = 0.0f;

	m_nextVtxU = 0;
	m_nextVtxV = 0;
	m_nextVtxColor = 0;

	m_bHaveColor = false;
	m_bHaveTex = false;
	field_26 = false;
	m_bBlockColor = false;
	field_28 = false;
	field_2C = 0;
	field_30 = 0;
	field_34 = false;

	m_vboCount = 1024;
	field_3C = -1;
	m_pVBOs = nullptr;

	field_48 = 0;
	m_maxVertices = 0;
	m_drawArraysMode = 0;
	m_accessMode = 0;

	m_maxVertices = allotedSize / sizeof(Vertex);
	m_accessMode = 2;

	m_pVBOs = new GLuint[m_vboCount];
	m_pVertices = new Vertex[m_maxVertices];
}

Tesselator::~Tesselator()
{
	if (m_pVBOs)
		delete[] m_pVBOs;
	if (m_pVertices)
		delete[] m_pVertices;
}

void Tesselator::addOffset(float x, float y, float z)
{
	m_offsetX += x;
	m_offsetY += y;
	m_offsetZ += z;
}

void Tesselator::clear()
{
	m_accessMode = 2;
	field_4 = 0;
	field_30 = 0;
	field_2C = 0;
	field_28 = 0;
}

void Tesselator::color(int r, int g, int b, int a)
{
	if (m_bBlockColor) return;

	if (b >= 255) b = 255;
	if (g >= 255) g = 255;
	if (r >= 255) r = 255;
	if (a >= 255) a = 255;

	// note: weird bit hacking (as in X & ~(X >> 31)) is used to perform the following:
	if (b < 0) b = 0;
	if (g < 0) g = 0;
	if (r < 0) r = 0;
	if (a < 0) a = 0;

	m_bHaveColor = true;
	m_nextVtxColor = a << 24 | b << 16 | g << 8 | r;
}

void Tesselator::color(int r, int g, int b)
{
	color(r, g, b, 255);
}

void Tesselator::color(int c, int a)
{
	color((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF, a);
}

void Tesselator::color(int c)
{
	color((c >> 16) & 0xFF, (c >> 8) & 0xFF, c & 0xFF, 255);
}

void Tesselator::color(char r, char g, char b)
{
	color(int(r), int(g), int(b));
}

void Tesselator::color(float r, float g, float b)
{
	color(r, g, b, 1.0f);
}

void Tesselator::color(float r, float g, float b, float a)
{
	color(int(r * 255), int(g * 255), int(b * 255), int(a * 255));
}

void Tesselator::begin()
{
	begin(GL_QUADS);
}

void Tesselator::begin(int drawMode)
{
	if (field_34 || field_28) return;

	field_34 = true;
	clear();
	m_drawArraysMode = drawMode;
	field_26 = 0;
	m_bHaveColor  = false;
	m_bHaveTex    = false;
	m_bBlockColor = false;
}

void Tesselator::draw()
{
	if (!field_34) return;
	if (field_28) return;

	field_34 = 0;

	if (field_4 > 0)
	{
		field_3C++;
		if (field_3C >= m_vboCount)
			field_3C = 0;

		xglBindBuffer(GL_ARRAY_BUFFER, m_pVBOs[field_3C]);
		xglBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * field_2C, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);

		if (m_bHaveTex)
		{
			// it won't use address 12, because we've bound a buffer object
			xglTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, m_u));
			xglEnableClientState(GL_TEXTURE_COORD_ARRAY);
		}
		if (m_bHaveColor)
		{
			// it won't use address 12, because we've bound a buffer object
			xglColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), (void*)offsetof(Vertex, m_color));
			xglEnableClientState(GL_COLOR_ARRAY);
		}

		xglVertexPointer(3, GL_FLOAT, sizeof(Vertex), (void*)offsetof(Vertex, m_x));
		xglEnableClientState(GL_VERTEX_ARRAY);

		// if we want to draw quads, draw triangles actually
		// otherwise, just pass the mode, it's fine
		if (m_drawArraysMode == GL_QUADS)
			xglDrawArrays(GL_TRIANGLES, 0, field_4);
		else
			xglDrawArrays(m_drawArraysMode, 0, field_4);

		xglDisableClientState(GL_VERTEX_ARRAY);
		if (m_bHaveColor)
			xglDisableClientState(GL_COLOR_ARRAY);
		if (m_bHaveTex)
			xglDisableClientState(GL_TEXTURE_COORD_ARRAY);
	}

	clear();
}

RenderChunk Tesselator::end(int vboIdx)
{
	if (!field_34 || field_28)
		return RenderChunk(); // empty render chunk

	int count = field_4;

	field_34 = 0;

	if (count > 0)
	{
		field_3C++;
		if (field_3C >= m_vboCount)
			field_3C = 0;
		if (vboIdx < 0)
			vboIdx = m_pVBOs[field_3C];

		xglBindBuffer(GL_ARRAY_BUFFER, vboIdx);
		xglBufferData(GL_ARRAY_BUFFER, sizeof (Vertex) * field_2C, m_pVertices, m_accessMode == 1 ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);

		field_48 += sizeof (Vertex) * field_2C;
	}

	clear();

	RenderChunk rchk(vboIdx, count);

	m_VboIdxToRenderChunkID[vboIdx] = rchk.m_id;

	return rchk;
}

int Tesselator::getVboCount()
{
	return m_vboCount;
}

void Tesselator::init()
{
	xglGenBuffers(m_vboCount, m_pVBOs);
}

void Tesselator::noColor()
{
	m_bBlockColor = true;
}

void Tesselator::normal(float x, float y, float z)
{
	// don't get the point of this
	dword_2514A4++;
}

void Tesselator::offset(float x, float y, float z)
{
	m_offsetX = x;
	m_offsetY = y;
	m_offsetZ = z;
}

void Tesselator::setAccessMode(int mode)
{
	m_accessMode = mode;
}

void Tesselator::tex(float u, float v)
{
	m_nextVtxU = u;
	m_nextVtxV = v;
	m_bHaveTex = true;
}

void Tesselator::vertexUV(float x, float y, float z, float u, float v)
{
	tex(u, v);
	vertex(x, y, z);
}

void Tesselator::vertex(float x, float y, float z)
{
	field_30++;
	if (m_drawArraysMode == GL_QUADS && !(field_30 << 30))
	{
		for (int v18 = 3; v18 != 1; v18--)
		{
			// why the hell is it doing this
			Vertex *pVert1 = &m_pVertices[field_2C - v18], *pVert2 = &m_pVertices[field_2C];
			if (m_bHaveTex)
			{
				pVert2->m_u = pVert1->m_u;
				pVert2->m_v = pVert1->m_v;
			}

			if (m_bHaveColor)
			{
				pVert2->m_color = pVert1->m_color;
			}

			pVert2->m_x = pVert1->m_x;
			pVert2->m_y = pVert1->m_y;
			pVert2->m_z = pVert1->m_z;

			field_4++;
			field_2C++;
		}
	}

	Vertex* pVert = &m_pVertices[field_2C];
	if (m_bHaveTex)
	{
		pVert->m_u = m_nextVtxU;
		pVert->m_v = m_nextVtxV;
	}

	if (m_bHaveColor)
	{
		pVert->m_color = m_nextVtxColor;
	}

	pVert->m_x = m_offsetX + x;
	pVert->m_y = m_offsetY + y;
	pVert->m_z = m_offsetZ + z;

	field_4++;
	field_2C++;

	if (!(field_4 & 3) && field_2C >= m_maxVertices - 1)
	{
		for (int i = 0; i < 3; i++)
			LOG_W("Overwriting the vertex buffer! This chunk/entity won't show up");

		clear();
	}
}

void Tesselator::voidBeginAndEndCalls(bool b)
{
	field_28 = b;
}
