// Setup Unit
#include "pch.h"

// Processing of BP address space registers Load Commands

using namespace Debug;

#define NO_VIEWPORT

namespace GX
{

	void GXCore::GL_SetScissor(int x, int y, int w, int h)
	{
		//h += 32;
#ifndef NO_VIEWPORT
		glScissor(x, scr_h - (h + y), w, h);
#endif
	}

	void GXCore::GL_SetCullMode(int mode)
	{
		switch(mode)
		{
			case GEN_REJECT_NONE:
				glDisable(GL_CULL_FACE);
				break;
			case GEN_REJECT_FRONT:
				// TODO: It's mixed up so far, not sure why, but it has to be that way
				glEnable(GL_CULL_FACE);
				glCullFace(GL_BACK);
				break;
			case GEN_REJECT_BACK:
				// TODO: It's mixed up so far, not sure why, but it has to be that way
				glEnable(GL_CULL_FACE);
				glCullFace(GL_FRONT);
				break;
			case GEN_REJECT_ALL:
				glEnable(GL_CULL_FACE);
				glCullFace(GL_FRONT_AND_BACK);
				break;
		}
	}

	// index range = 00..FF
	// reg size = 24 bit (value is already masked)
	void GXCore::loadBPReg(size_t index, uint32_t value)
	{
		bpLoads++;

		if (GpRegsLog)
		{
			Report(Channel::GP, "Load BP: index: 0x%02X, data: 0x%08X\n", index, value);
		}

		switch (index)
		{
			//
			// gen mode
			//

			case GEN_MODE_ID:
			{
				genmode.bits = value;
				GL_SetCullMode(genmode.reject_en);
			}
			return;

			// I don't see any use for MSLOC yet, I added it to avoid spamming with warnings

			case GEN_MSLOC0_ID:
				msloc[0].bits = value;
				break;
			case GEN_MSLOC1_ID:
				msloc[1].bits = value;
				break;
			case GEN_MSLOC2_ID:
				msloc[2].bits = value;
				break;
			case GEN_MSLOC3_ID:
				msloc[3].bits = value;
				break;

			//
			// set scissor box
			//

			case SU_SCIS0_ID:
			{
				int x, y, w, h;

				scis0.bits = value;

				x = scis0.sux - 342;
				y = scis0.suy - 342;
				w = scis1.suw - scis0.sux + 1;
				h = scis1.suh - scis0.suy + 1;

				//GFXError("scissor (%i, %i)-(%i, %i)", x, y, w, h);
				GL_SetScissor(x, y, w, h);
			}
			return;

			case SU_SCIS1_ID:
			{
				int x, y, w, h;

				scis1.bits = value;

				x = scis0.sux - 342;
				y = scis0.suy - 342;
				w = scis1.suw - scis0.sux + 1;
				h = scis1.suh - scis0.suy + 1;

				//GFXError("scissor (%i, %i)-(%i, %i)", x, y, w, h);
				GL_SetScissor(x, y, w, h);
			}
			return;

			// Pixel Engine block

			case PE_ZMODE_ID:
			{
				static const char* zf[] = {
					"NEVER",
					"LESS",
					"EQUAL",
					"LEQUAL",
					"GREATER",
					"NEQUAL",
					"GEQUAL",
					"ALWAYS"
				};

				static uint32_t glzf[] = {
					GL_NEVER,
					GL_LESS,
					GL_EQUAL,
					GL_LEQUAL,
					GL_GREATER,
					GL_NOTEQUAL,
					GL_GEQUAL,
					GL_ALWAYS
				};

				pe.zmode.bits = value;

				/*/
				GFXError(
					"z mode:\n"
					"compare: %s\n"
					"func: %s\n"
					"update: %s",
					(bpRegs.zmode.enable) ? ("yes") : ("no"),
					zf[bpRegs.zmode.func],
					(bpRegs.zmode.mask) ? ("yes") : ("no")
				);
				/*/

				if (pe.zmode.enable)
				{
					glEnable(GL_DEPTH_TEST);
					glDepthFunc(glzf[pe.zmode.func]);
					glDepthMask(pe.zmode.mask);
				}
				else glDisable(GL_DEPTH_TEST);
			}
			return;

			// set blending rules
			case PE_CMODE0_ID:
			{
				pe.cmode0.bits = value;

				static const char* logicop[] = {
					"clear",
					"and",
					"revand",
					"copy",
					"invand",
					"nop",
					"xor",
					"or",
					"nor",
					"eqv",
					"inv",
					"revor",
					"invcopy",
					"invor",
					"nand",
					"set"
				};

				static const char* sfactor[] = {
					"zero",
					"one",
					"srcclr",
					"invsrcclr",
					"srcalpha",
					"invsrcalpha",
					"dstalpha",
					"invdstalpha"
				};

				static const char* dfactor[] = {
					"zero",
					"one",
					"dstclr",
					"invdstclr",
					"srcalpha",
					"invsrcalpha",
					"dstalpha",
					"invdstalpha"
				};

				/*/
				GFXError(
					"blend rules\n\n"
					"blend:%s, logic:%s\n"
					"logic op : %s\n"
					"sfactor : %s\n"
					"dfactor : %s\n",
					(bpRegs.cmode0.blend_en) ? ("on") : ("off"),
					(bpRegs.cmode0.logop_en) ? ("on") : ("off"),
					logicop[bpRegs.cmode0.logop],
					sfactor[bpRegs.cmode0.sfactor],
					dfactor[bpRegs.cmode0.dfactor]
				);
				/*/

				static uint32_t glsf[] = {
					GL_ZERO,
					GL_ONE,
					GL_SRC_COLOR,
					GL_ONE_MINUS_SRC_COLOR,
					GL_SRC_ALPHA,
					GL_ONE_MINUS_SRC_ALPHA,
					GL_DST_ALPHA,
					GL_ONE_MINUS_DST_ALPHA
				};

				static uint32_t gldf[] = {
					GL_ZERO,
					GL_ONE,
					GL_DST_COLOR,
					GL_ONE_MINUS_DST_COLOR,
					GL_SRC_ALPHA,
					GL_ONE_MINUS_SRC_ALPHA,
					GL_DST_ALPHA,
					GL_ONE_MINUS_DST_ALPHA
				};

				// blend hack
				if (pe.cmode0.blend_en)
				{
					glEnable(GL_BLEND);
					glBlendFunc(glsf[pe.cmode0.sfactor], gldf[pe.cmode0.dfactor]);
				}
				else glDisable(GL_BLEND);

				static uint32_t logop[] = {
					GL_CLEAR,
					GL_AND,
					GL_AND_REVERSE,
					GL_COPY,
					GL_AND_INVERTED,
					GL_NOOP,
					GL_XOR,
					GL_OR,
					GL_NOR,
					GL_EQUIV,
					GL_INVERT,
					GL_OR_REVERSE,
					GL_COPY_INVERTED,
					GL_OR_INVERTED,
					GL_NAND,
					GL_SET
				};

				// logic operations
				if (pe.cmode0.logop_en)
				{
					glEnable(GL_COLOR_LOGIC_OP);
					glLogicOp(logop[pe.cmode0.logop]);
				}
				else glDisable(GL_COLOR_LOGIC_OP);
			}
			return;

			case PE_CMODE1_ID:
				pe.cmode1.bits = value;
				break;

			// TODO: Make a GP update when copying the frame buffer by Pixel Engine.

			// draw done
			case PE_FINISH_ID:
			{
				GPFrameDone();

				pe_done_num++;
				if (pe_done_num == 1)
				{
					vi.xfb = false;     // disable VI output
				}
				DONE_INT();
			}
			break;

			case PE_TOKEN_ID:
			{
				pe.token.bits = value;
				if (pe.token.token == pe.token_int.token)
				{
					GPFrameDone();

					vi.xfb = false;     // disable VI output
					TOKEN_INT();
				}
			}
			break;

			// token
			case PE_TOKEN_INT_ID:
				pe.token_int.bits = value;
				break;





			case PE_COPY_CLEAR_AR_ID:
				pe.copy_clear_ar.bits = value;
				break;

			case PE_COPY_CLEAR_GB_ID:
				pe.copy_clear_gb.bits = value;
				break;

			case PE_COPY_CLEAR_Z_ID:
				pe.copy_clear_z.bits = value;
				break;

			//
			// SetImage0. texture image width, height, format
			//

			case TX_SETIMAGE0_I0_ID:
			{
				teximg0[0].bits = value;
				texvalid[0][0] = true;
				tryLoadTex(0);
			}
			return;

			case TX_SETIMAGE0_I1_ID:
			{
				teximg0[1].bits = value;
				texvalid[0][1] = true;
				//tryLoadTex(1);
			}
			return;

			case TX_SETIMAGE0_I2_ID:
			{
				teximg0[2].bits = value;
				texvalid[0][2] = true;
				//tryLoadTex(2);
			}
			return;

			case TX_SETIMAGE0_I3_ID:
			{
				teximg0[3].bits = value;
				texvalid[0][3] = true;
				//tryLoadTex(3);
			}
			return;

			case TX_SETIMAGE0_I4_ID:
			{
				teximg0[4].bits = value;
				texvalid[0][4] = true;
				//tryLoadTex(4);
			}
			return;

			case TX_SETIMAGE0_I5_ID:
			{
				teximg0[5].bits = value;
				texvalid[0][5] = true;
				//tryLoadTex(5);
			}
			return;

			case TX_SETIMAGE0_I6_ID:
			{
				teximg0[6].bits = value;
				texvalid[0][6] = true;
				//tryLoadTex(6);
			}
			return;

			case TX_SETIMAGE0_I7_ID:
			{
				teximg0[7].bits = value;
				texvalid[0][7] = true;
				//tryLoadTex(7);
			}
			return;

			// SetImage1

			// SetImage2

			//
			// SetImage3. texture image base
			//

			case TX_SETIMAGE3_I0_ID:
			{
				teximg3[0].bits = value;
				texvalid[3][0] = true;
				tryLoadTex(0);
			}
			return;

			case TX_SETIMAGE3_I1_ID:
			{
				teximg3[1].bits = value;
				texvalid[3][1] = true;
				//tryLoadTex(1);
			}
			return;

			case TX_SETIMAGE3_I2_ID:
			{
				teximg3[2].bits = value;
				texvalid[3][2] = true;
				//tryLoadTex(2);
			}
			return;

			case TX_SETIMAGE3_I3_ID:
			{
				teximg3[3].bits = value;
				texvalid[3][3] = true;
				//tryLoadTex(3);
			}
			return;

			case TX_SETIMAGE3_I4_ID:
			{
				teximg3[4].bits = value;
				texvalid[3][4] = true;
				//tryLoadTex(4);
			}
			return;

			case TX_SETIMAGE3_I5_ID:
			{
				teximg3[5].bits = value;
				texvalid[3][5] = true;
				//tryLoadTex(5);
			}
			return;

			case TX_SETIMAGE3_I6_ID:
			{
				teximg3[6].bits = value;
				texvalid[3][6] = true;
				//tryLoadTex(6);
			}
			return;

			case TX_SETIMAGE3_I7_ID:
			{
				teximg3[7].bits = value;
				texvalid[3][7] = true;
				//tryLoadTex(7);
			}
			return;

			//
			// load tlut
			//

			case TX_LOADTLUT0_ID:
			{
				loadtlut0.bits = value;

				LoadTlut(
					(loadtlut0.base << 5),   // ram address
					(loadtlut1.tmem << 9),   // tlut offset
					loadtlut1.count          // tlut size
				);
			}
			return;

			case TX_LOADTLUT1_ID:
			{
				loadtlut1.bits = value;

				LoadTlut(
					(loadtlut0.base << 5),   // ram address
					(loadtlut1.tmem << 9),   // tlut offset
					loadtlut1.count          // tlut size
				);
			}
			return;

			//
			// set tlut
			//

			case TX_SETTLUT_I0_ID:
			{
				settlut[0].bits = value;
			}
			return;

			//
			// set texture modes
			//

			case TX_SETMODE0_I0_ID:
			{
				texmode0[0].bits = value;
			}
			return;

			//
			// texture coord scale
			//

			case SU_SSIZE0_ID:
			case SU_SSIZE1_ID:
			case SU_SSIZE2_ID:
			case SU_SSIZE3_ID:
			case SU_SSIZE4_ID:
			case SU_SSIZE5_ID:
			case SU_SSIZE6_ID:
			case SU_SSIZE7_ID:
			{
				int num = (index >> 1) & 1;
				ssize[num].bits = value;
			}
			return;

			case SU_TSIZE0_ID:
			case SU_TSIZE1_ID:
			case SU_TSIZE2_ID:
			case SU_TSIZE3_ID:
			case SU_TSIZE4_ID:
			case SU_TSIZE5_ID:
			case SU_TSIZE6_ID:
			case SU_TSIZE7_ID:
			{
				int num = (index >> 1) & 1;
				tsize[num].bits = value;
			}
			return;

#pragma region "TEV bypass"

			case TEV_COLOR_ENV_0_ID: tev.color_env[0].bits = value; break;
			case TEV_ALPHA_ENV_0_ID: tev.alpha_env[0].bits = value; break;
			case TEV_COLOR_ENV_1_ID: tev.color_env[1].bits = value; break;
			case TEV_ALPHA_ENV_1_ID: tev.alpha_env[1].bits = value; break;
			case TEV_COLOR_ENV_2_ID: tev.color_env[2].bits = value; break;
			case TEV_ALPHA_ENV_2_ID: tev.alpha_env[2].bits = value; break;
			case TEV_COLOR_ENV_3_ID: tev.color_env[3].bits = value; break;
			case TEV_ALPHA_ENV_3_ID: tev.alpha_env[3].bits = value; break;
			case TEV_COLOR_ENV_4_ID: tev.color_env[4].bits = value; break;
			case TEV_ALPHA_ENV_4_ID: tev.alpha_env[4].bits = value; break;
			case TEV_COLOR_ENV_5_ID: tev.color_env[5].bits = value; break;
			case TEV_ALPHA_ENV_5_ID: tev.alpha_env[5].bits = value; break;
			case TEV_COLOR_ENV_6_ID: tev.color_env[6].bits = value; break;
			case TEV_ALPHA_ENV_6_ID: tev.alpha_env[6].bits = value; break;
			case TEV_COLOR_ENV_7_ID: tev.color_env[7].bits = value; break;
			case TEV_ALPHA_ENV_7_ID: tev.alpha_env[7].bits = value; break;
			case TEV_COLOR_ENV_8_ID: tev.color_env[8].bits = value; break;
			case TEV_ALPHA_ENV_8_ID: tev.alpha_env[8].bits = value; break;
			case TEV_COLOR_ENV_9_ID: tev.color_env[9].bits = value; break;
			case TEV_ALPHA_ENV_9_ID: tev.alpha_env[9].bits = value; break;
			case TEV_COLOR_ENV_A_ID: tev.color_env[0xa].bits = value; break;
			case TEV_ALPHA_ENV_A_ID: tev.alpha_env[0xa].bits = value; break;
			case TEV_COLOR_ENV_B_ID: tev.color_env[0xb].bits = value; break;
			case TEV_ALPHA_ENV_B_ID: tev.alpha_env[0xb].bits = value; break;
			case TEV_COLOR_ENV_C_ID: tev.color_env[0xc].bits = value; break;
			case TEV_ALPHA_ENV_C_ID: tev.alpha_env[0xc].bits = value; break;
			case TEV_COLOR_ENV_D_ID: tev.color_env[0xd].bits = value; break;
			case TEV_ALPHA_ENV_D_ID: tev.alpha_env[0xd].bits = value; break;
			case TEV_COLOR_ENV_E_ID: tev.color_env[0xe].bits = value; break;
			case TEV_ALPHA_ENV_E_ID: tev.alpha_env[0xe].bits = value; break;
			case TEV_COLOR_ENV_F_ID: tev.color_env[0xf].bits = value; break;
			case TEV_ALPHA_ENV_F_ID: tev.alpha_env[0xf].bits = value; break;

			case TEV_REGISTERL_0_ID: tev.regl[0].bits = value; break;
			case TEV_REGISTERH_0_ID: tev.regh[0].bits = value; break;
			case TEV_REGISTERL_1_ID: tev.regl[1].bits = value; break;
			case TEV_REGISTERH_1_ID: tev.regh[1].bits = value; break;
			case TEV_REGISTERL_2_ID: tev.regl[2].bits = value; break;
			case TEV_REGISTERH_2_ID: tev.regh[2].bits = value; break;
			case TEV_REGISTERL_3_ID: tev.regl[3].bits = value; break;
			case TEV_REGISTERH_3_ID: tev.regh[3].bits = value; break;
			case TEV_RANGE_ADJ_C_ID: tev.rangeadj_control.bits = value; break;
			case TEV_RANGE_ADJ_0_ID: tev.range_adj[0].bits = value; break;
			case TEV_RANGE_ADJ_1_ID: tev.range_adj[1].bits = value; break;
			case TEV_RANGE_ADJ_2_ID: tev.range_adj[2].bits = value; break;
			case TEV_RANGE_ADJ_3_ID: tev.range_adj[3].bits = value; break;
			case TEV_RANGE_ADJ_4_ID: tev.range_adj[4].bits = value; break;
			case TEV_FOG_PARAM_0_ID: tev.fog_param0.bits = value; break;
			case TEV_FOG_PARAM_1_ID: tev.fog_param1.bits = value; break;
			case TEV_FOG_PARAM_2_ID: tev.fog_param2.bits = value; break;
			case TEV_FOG_PARAM_3_ID: tev.fog_param3.bits = value; break;
			case TEV_FOG_COLOR_ID: tev.fog_color.bits = value; break;
			case TEV_ALPHAFUNC_ID: tev.alpha_func.bits = value; break;
			case TEV_Z_ENV_0_ID: tev.zenv0.bits = value; break;
			case TEV_Z_ENV_1_ID: tev.zenv1.bits = value; break;
			case TEV_KSEL_0_ID: tev.ksel[0].bits = value; break;
			case TEV_KSEL_1_ID: tev.ksel[1].bits = value; break;
			case TEV_KSEL_2_ID: tev.ksel[2].bits = value; break;
			case TEV_KSEL_3_ID: tev.ksel[3].bits = value; break;
			case TEV_KSEL_4_ID: tev.ksel[4].bits = value; break;
			case TEV_KSEL_5_ID: tev.ksel[5].bits = value; break;
			case TEV_KSEL_6_ID: tev.ksel[6].bits = value; break;
			case TEV_KSEL_7_ID: tev.ksel[7].bits = value; break;

#pragma endregion "TEV bypass"

			default:
			{
				Report(Channel::GP, "Unknown BP load, index: 0x%02X\n", index);
				break;
			}
		}
	}
}
