#include "ibm.h"
#include "device.h"
#include "mem.h"
#include "thread.h"
#include "video.h"
#include "vid_svga.h"
#include "vid_voodoo.h"
#include "vid_voodoo_common.h"
#include "vid_voodoo_regs.h"
#include "vid_voodoo_render.h"
#include "vid_voodoo_setup.h"

void voodoo_triangle_setup(voodoo_t *voodoo) {
	float dxAB, dxBC, dyAB, dyBC;
	float area;
	int va = 0, vb = 1, vc = 2;
	vert_t verts[3];

	verts[0] = voodoo->verts[0];
	verts[1] = voodoo->verts[1];
	verts[2] = voodoo->verts[2];

	if (verts[0].sVy < verts[1].sVy) {
		if (verts[1].sVy < verts[2].sVy) {
			/* V1>V0, V2>V1, V2>V1>V0*/
			va = 0; /*OK*/
			vb = 1;
			vc = 2;
		} else {
			/* V1>V0, V1>V2*/
			if (verts[0].sVy < verts[2].sVy) {
				/* V1>V0, V1>V2, V2>V0, V1>V2>V0*/
				va = 0;
				vb = 2;
				vc = 1;
			} else {
				/* V1>V0, V1>V2, V0>V2, V1>V0>V2*/
				va = 2;
				vb = 0;
				vc = 1;
			}
		}
	} else {
		if (verts[1].sVy < verts[2].sVy) {
			/* V0>V1, V2>V1*/
			if (verts[0].sVy < verts[2].sVy) {
				/* V0>V1, V2>V1, V2>V0, V2>V0>V1*/
				va = 1;
				vb = 0;
				vc = 2;
			} else {
				/* V0>V1, V2>V1, V0>V2, V0>V2>V1*/
				va = 1;
				vb = 2;
				vc = 0;
			}
		} else {
			/*V0>V1>V2*/
			va = 2;
			vb = 1;
			vc = 0;
		}
	}

	dxAB = verts[0].sVx - verts[1].sVx;
	dxBC = verts[1].sVx - verts[2].sVx;
	dyAB = verts[0].sVy - verts[1].sVy;
	dyBC = verts[1].sVy - verts[2].sVy;

	area = dxAB * dyBC - dxBC * dyAB;

	if (area == 0.0)
		return;

	if (voodoo->sSetupMode & SETUPMODE_CULLING_ENABLE) {
		int cull_sign = voodoo->sSetupMode & SETUPMODE_CULLING_SIGN;
		int sign = (area < 0.0);

		if ((voodoo->sSetupMode & (SETUPMODE_CULLING_ENABLE | SETUPMODE_DISABLE_PINGPONG))
			== SETUPMODE_CULLING_ENABLE && voodoo->cull_pingpong)
			cull_sign = !cull_sign;

		if (cull_sign && sign)
			return;
		if (!cull_sign && !sign)
			return;
	}

	dxAB = verts[va].sVx - verts[vb].sVx;
	dxBC = verts[vb].sVx - verts[vc].sVx;
	dyAB = verts[va].sVy - verts[vb].sVy;
	dyBC = verts[vb].sVy - verts[vc].sVy;

	area = dxAB * dyBC - dxBC * dyAB;

	dxAB /= area;
	dxBC /= area;
	dyAB /= area;
	dyBC /= area;

	voodoo->params.vertexAx = (int32_t)(int16_t)((int32_t)(verts[va].sVx * 16.0f) & 0xffff);
	voodoo->params.vertexAy = (int32_t)(int16_t)((int32_t)(verts[va].sVy * 16.0f) & 0xffff);
	voodoo->params.vertexBx = (int32_t)(int16_t)((int32_t)(verts[vb].sVx * 16.0f) & 0xffff);
	voodoo->params.vertexBy = (int32_t)(int16_t)((int32_t)(verts[vb].sVy * 16.0f) & 0xffff);
	voodoo->params.vertexCx = (int32_t)(int16_t)((int32_t)(verts[vc].sVx * 16.0f) & 0xffff);
	voodoo->params.vertexCy = (int32_t)(int16_t)((int32_t)(verts[vc].sVy * 16.0f) & 0xffff);

	if (voodoo->params.vertexAy > voodoo->params.vertexBy || voodoo->params.vertexBy > voodoo->params.vertexCy) {
		pclog("triangle_setup wrong order %d %d %d\n", voodoo->params.vertexAy, voodoo->params.vertexBy, voodoo->params.vertexCy);
		return;
	}

	if (voodoo->sSetupMode & SETUPMODE_RGB) {
		voodoo->params.startR = (int32_t)(verts[va].sRed * 4096.0f);
		voodoo->params.dRdX = (int32_t)(((verts[va].sRed - verts[vb].sRed) * dyBC - (verts[vb].sRed - verts[vc].sRed) * dyAB) * 4096.0f);
		voodoo->params.dRdY = (int32_t)(((verts[vb].sRed - verts[vc].sRed) * dxAB - (verts[va].sRed - verts[vb].sRed) * dxBC) * 4096.0f);
		voodoo->params.startG = (int32_t)(verts[va].sGreen * 4096.0f);
		voodoo->params.dGdX = (int32_t)(((verts[va].sGreen - verts[vb].sGreen) * dyBC - (verts[vb].sGreen - verts[vc].sGreen) * dyAB) * 4096.0f);
		voodoo->params.dGdY = (int32_t)(((verts[vb].sGreen - verts[vc].sGreen) * dxAB - (verts[va].sGreen - verts[vb].sGreen) * dxBC) * 4096.0f);
		voodoo->params.startB = (int32_t)(verts[va].sBlue * 4096.0f);
		voodoo->params.dBdX = (int32_t)(((verts[va].sBlue - verts[vb].sBlue) * dyBC - (verts[vb].sBlue - verts[vc].sBlue) * dyAB) * 4096.0f);
		voodoo->params.dBdY = (int32_t)(((verts[vb].sBlue - verts[vc].sBlue) * dxAB - (verts[va].sBlue - verts[vb].sBlue) * dxBC) * 4096.0f);
	}
	if (voodoo->sSetupMode & SETUPMODE_ALPHA) {
		voodoo->params.startA = (int32_t)(verts[va].sAlpha * 4096.0f);
		voodoo->params.dAdX = (int32_t)(((verts[va].sAlpha - verts[vb].sAlpha) * dyBC - (verts[vb].sAlpha - verts[vc].sAlpha) * dyAB) * 4096.0f);
		voodoo->params.dAdY = (int32_t)(((verts[vb].sAlpha - verts[vc].sAlpha) * dxAB - (verts[va].sAlpha - verts[vb].sAlpha) * dxBC) * 4096.0f);
	}
	if (voodoo->sSetupMode & SETUPMODE_Z) {
		voodoo->params.startZ = (int32_t)(verts[va].sVz * 4096.0f);
		voodoo->params.dZdX = (int32_t)(((verts[va].sVz - verts[vb].sVz) * dyBC - (verts[vb].sVz - verts[vc].sVz) * dyAB) * 4096.0f);
		voodoo->params.dZdY = (int32_t)(((verts[vb].sVz - verts[vc].sVz) * dxAB - (verts[va].sVz - verts[vb].sVz) * dxBC) * 4096.0f);
	}
	if (voodoo->sSetupMode & SETUPMODE_Wb) {
		voodoo->params.startW = (int64_t)(verts[va].sWb * 4294967296.0f);
		voodoo->params.dWdX = (int64_t)(((verts[va].sWb - verts[vb].sWb) * dyBC - (verts[vb].sWb - verts[vc].sWb) * dyAB) * 4294967296.0f);
		voodoo->params.dWdY = (int64_t)(((verts[vb].sWb - verts[vc].sWb) * dxAB - (verts[va].sWb - verts[vb].sWb) * dxBC) * 4294967296.0f);
		voodoo->params.tmu[0].startW = voodoo->params.tmu[1].startW = voodoo->params.startW;
		voodoo->params.tmu[0].dWdX = voodoo->params.tmu[1].dWdX = voodoo->params.dWdX;
		voodoo->params.tmu[0].dWdY = voodoo->params.tmu[1].dWdY = voodoo->params.dWdY;
	}
	if (voodoo->sSetupMode & SETUPMODE_W0) {
		voodoo->params.tmu[0].startW = (int64_t)(verts[va].sW0 * 4294967296.0f);
		voodoo->params.tmu[0].dWdX = (int64_t)(((verts[va].sW0 - verts[vb].sW0) * dyBC - (verts[vb].sW0 - verts[vc].sW0) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[0].dWdY = (int64_t)(((verts[vb].sW0 - verts[vc].sW0) * dxAB - (verts[va].sW0 - verts[vb].sW0) * dxBC) * 4294967296.0f);
		voodoo->params.tmu[1].startW = voodoo->params.tmu[0].startW;
		voodoo->params.tmu[1].dWdX = voodoo->params.tmu[0].dWdX;
		voodoo->params.tmu[1].dWdY = voodoo->params.tmu[0].dWdY;
	}
	if (voodoo->sSetupMode & SETUPMODE_S0_T0) {
		voodoo->params.tmu[0].startS = (int64_t)(verts[va].sS0 * 4294967296.0f);
		voodoo->params.tmu[0].dSdX = (int64_t)(((verts[va].sS0 - verts[vb].sS0) * dyBC - (verts[vb].sS0 - verts[vc].sS0) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[0].dSdY = (int64_t)(((verts[vb].sS0 - verts[vc].sS0) * dxAB - (verts[va].sS0 - verts[vb].sS0) * dxBC) * 4294967296.0f);
		voodoo->params.tmu[0].startT = (int64_t)(verts[va].sT0 * 4294967296.0f);
		voodoo->params.tmu[0].dTdX = (int64_t)(((verts[va].sT0 - verts[vb].sT0) * dyBC - (verts[vb].sT0 - verts[vc].sT0) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[0].dTdY = (int64_t)(((verts[vb].sT0 - verts[vc].sT0) * dxAB - (verts[va].sT0 - verts[vb].sT0) * dxBC) * 4294967296.0f);
		voodoo->params.tmu[1].startS = voodoo->params.tmu[0].startS;
		voodoo->params.tmu[1].dSdX = voodoo->params.tmu[0].dSdX;
		voodoo->params.tmu[1].dSdY = voodoo->params.tmu[0].dSdY;
		voodoo->params.tmu[1].startT = voodoo->params.tmu[0].startT;
		voodoo->params.tmu[1].dTdX = voodoo->params.tmu[0].dTdX;
		voodoo->params.tmu[1].dTdY = voodoo->params.tmu[0].dTdY;
	}
	if (voodoo->sSetupMode & SETUPMODE_W1) {
		voodoo->params.tmu[1].startW = (int64_t)(verts[va].sW1 * 4294967296.0f);
		voodoo->params.tmu[1].dWdX = (int64_t)(((verts[va].sW1 - verts[vb].sW1) * dyBC - (verts[vb].sW1 - verts[vc].sW1) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[1].dWdY = (int64_t)(((verts[vb].sW1 - verts[vc].sW1) * dxAB - (verts[va].sW1 - verts[vb].sW1) * dxBC) * 4294967296.0f);
	}
	if (voodoo->sSetupMode & SETUPMODE_S1_T1) {
		voodoo->params.tmu[1].startS = (int64_t)(verts[va].sS1 * 4294967296.0f);
		voodoo->params.tmu[1].dSdX = (int64_t)(((verts[va].sS1 - verts[vb].sS1) * dyBC - (verts[vb].sS1 - verts[vc].sS1) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[1].dSdY = (int64_t)(((verts[vb].sS1 - verts[vc].sS1) * dxAB - (verts[va].sS1 - verts[vb].sS1) * dxBC) * 4294967296.0f);
		voodoo->params.tmu[1].startT = (int64_t)(verts[va].sT1 * 4294967296.0f);
		voodoo->params.tmu[1].dTdX = (int64_t)(((verts[va].sT1 - verts[vb].sT1) * dyBC - (verts[vb].sT1 - verts[vc].sT1) * dyAB) * 4294967296.0f);
		voodoo->params.tmu[1].dTdY = (int64_t)(((verts[vb].sT1 - verts[vc].sT1) * dxAB - (verts[va].sT1 - verts[vb].sT1) * dxBC) * 4294967296.0f);
	}

	voodoo->params.sign = (area < 0.0);

	if (voodoo->ncc_dirty[0])
		voodoo_update_ncc(voodoo, 0);
	if (voodoo->ncc_dirty[1])
		voodoo_update_ncc(voodoo, 1);
	voodoo->ncc_dirty[0] = voodoo->ncc_dirty[1] = 0;

	voodoo_queue_triangle(voodoo, &voodoo->params);
}
