#include "emu.h"
#include "includes/n64.h"
#include "video/n64.h"

namespace N64
{

namespace RDP
{

#define LookUpCC(A, B, C, D) m_rdp->GetCCLUT2()[(m_rdp->GetCCLUT1()[(A << 16) | (B << 8) | C] << 8) | D]

void Processor::RenderSpans(int start, int end, int tilenum, bool flip)
{
	int clipy1 = GetScissor()->m_yh;
	int clipy2 = GetScissor()->m_yl;

	if (clipy2 <= 0)
	{
		return;
	}

	if (start < clipy1)
	{
		start = clipy1;
	}
	if (start >= clipy2)
	{
		start = clipy2 - 1;
	}
	if (end < clipy1)
	{
		end = clipy1;
	}
	if (end >= clipy2) // Needed by 40 Winks
	{
		end = clipy2 - 1;
	}

	for(int i = start; i <= end; i++)
	{
		switch(OtherModes.cycle_type)
		{
			case CYCLE_TYPE_1: Spans[i].Draw1Cycle(i, tilenum, flip); break;
			case CYCLE_TYPE_2: Spans[i].Draw2Cycle(i, tilenum, flip); break;
			case CYCLE_TYPE_COPY: Spans[i].DrawCopy(i, tilenum, flip); break;
			case CYCLE_TYPE_FILL: Spans[i].DrawFill(i, tilenum, flip); break;
		}
	}
}

void Span::Dump()
{
	printf("    m_lx = %d\n", m_lx);
	printf("    m_rx = %d\n",  m_rx);
	printf("    m_s.w = %08x\n", m_s.w);
	printf("    m_t.w = %08x\n", m_t.w);
	printf("    m_w.w = %08x\n", m_w.w);
	printf("    m_r.w = %08x\n", m_r.w);
	printf("    m_g.w = %08x\n", m_g.w);
	printf("    m_b.w = %08x\n", m_b.w);
	printf("    m_a.w = %08x\n", m_a.w);
	printf("    m_z.w = %08x\n", m_z.w);

	printf("    CVG: ");
	for(int index = 0; index < RDP_CVG_SPAN_MAX; index++)
	{
		printf("%d", m_cvg[index]);
	}
	printf("\n");
}

void Span::SetMachine(running_machine &machine)
{
	_n64_state *state = machine.driver_data<_n64_state>();

	m_machine = &machine;
	m_rdp = &state->m_rdp;
}

void Span::RGBAZClip(int sr, int sg, int sb, int sa, int *sz)
{
	m_rdp->ShadeColor.i.r = m_rdp->GetSpecial9BitClampTable()[sr & 0x1ff];
	m_rdp->ShadeColor.i.g = m_rdp->GetSpecial9BitClampTable()[sg & 0x1ff];
	m_rdp->ShadeColor.i.b = m_rdp->GetSpecial9BitClampTable()[sb & 0x1ff];
	m_rdp->ShadeColor.i.a = m_rdp->GetSpecial9BitClampTable()[sa & 0x1ff];

	INT32 zanded = (*sz) & 0x60000;

	zanded >>= 17;
	switch(zanded)
	{
		case 0: *sz &= 0x3ffff;											break;
		case 1:	*sz &= 0x3ffff;											break;
		case 2: *sz = 0x3ffff;											break;
		case 3: *sz = 0x3ffff;											break;
	}
}

void Span::RGBAZCorrectTriangle(INT32 offx, INT32 offy, INT32* r, INT32* g, INT32* b, INT32* a, INT32* z)
{
	if (m_rdp->MiscState.CurrentPixCvg == 8)
	{
		*r >>= 2;
		*g >>= 2;
		*b >>= 2;
		*a >>= 2;
		*z = (*z >> 3) & 0x7ffff;
	}
	else
	{
		INT32 summand_xr = offx * SIGN13(m_rdp->m_span_dr >> 14);
		INT32 summand_yr = offy * SIGN13(m_rdp->m_span_drdy >> 14);
		INT32 summand_xb = offx * SIGN13(m_rdp->m_span_db >> 14);
		INT32 summand_yb = offy * SIGN13(m_rdp->m_span_dbdy >> 14);
		INT32 summand_xg = offx * SIGN13(m_rdp->m_span_dg >> 14);
		INT32 summand_yg = offy * SIGN13(m_rdp->m_span_dgdy >> 14);
		INT32 summand_xa = offx * SIGN13(m_rdp->m_span_da >> 14);
		INT32 summand_ya = offy * SIGN13(m_rdp->m_span_dady >> 14);

		INT32 summand_xz = offx * SIGN22(m_rdp->m_span_dz >> 10);
		INT32 summand_yz = offy * SIGN22(m_rdp->m_span_dzdy >> 10);

		*r = ((*r << 2) + summand_xr + summand_yr) >> 4;
		*g = ((*g << 2) + summand_xg + summand_yg) >> 4;
		*b = ((*b << 2) + summand_xb + summand_yb) >> 4;
		*a = ((*a << 2) + summand_xa + summand_ya) >> 4;
		*z = (((*z << 2) + summand_xz + summand_yz) >> 5) & 0x7ffff;
	}
}

void Span::Draw1Cycle(int index, int tilenum, bool flip)
{
	int clipx1 = m_rdp->GetScissor()->m_xh;
	int clipx2 = m_rdp->GetScissor()->m_xl;

	SpanParam r = m_r;
	SpanParam g = m_g;
	SpanParam b = m_b;
	SpanParam a = m_a;
	SpanParam z = m_z;
	SpanParam s = m_s;
	SpanParam t = m_t;
	SpanParam w = m_w;

	UINT32 zb = m_rdp->MiscState.ZBAddress >> 1;
	UINT32 zhb = zb;
	UINT8 offx = 0, offy = 0;

	INT32 tile1 = tilenum;

	m_rdp->TexPipe.CalculateClampDiffs(tile1);

	bool noisecompute = m_rdp->ColorInputs.combiner_rgbsub_a_r[1] == &m_rdp->NoiseColor.i.r;
	bool partialreject = (m_rdp->ColorInputs.blender2b_a[0] == &m_rdp->InvPixelColor.i.a && m_rdp->ColorInputs.blender1b_a[0] == &m_rdp->PixelColor.i.a);
	bool bsel0 = (m_rdp->ColorInputs.blender2b_a[0] == &m_rdp->MemoryColor.i.a);

	int drinc = flip ? (m_rdp->m_span_dr) : -m_rdp->m_span_dr;
	int dginc = flip ? (m_rdp->m_span_dg) : -m_rdp->m_span_dg;
	int dbinc = flip ? (m_rdp->m_span_db) : -m_rdp->m_span_db;
	int dainc = flip ? (m_rdp->m_span_da) : -m_rdp->m_span_da;
	int dzinc = flip ? (m_rdp->m_span_dz) : -m_rdp->m_span_dz;
	int dsinc = flip ? (m_rdp->m_span_ds) : -m_rdp->m_span_ds;
	int dtinc = flip ? (m_rdp->m_span_dt) : -m_rdp->m_span_dt;
	int dwinc = flip ? (m_rdp->m_span_dw) : -m_rdp->m_span_dw;
	int dzpix = m_rdp->m_span_dzpix;
	int xinc = flip ? 1 : -1;

	int fb_index = m_rdp->MiscState.FBWidth * index;

	int cdith = 0;
	int adith = 0;

	int xstart = m_lx;
	int xend = m_unscissored_rx;
	int xend_scissored = m_rx;

	int x = xend;

	int length = flip ? (xstart - xend) : (xend - xstart);
	m_rdp->TexPipe.m_start_span = true;
	UINT32 fir, fig, fib;

	for (int j = 0; j <= length; j++)
	{
		int sr = r.w >> 14;
		int sg = g.w >> 14;
		int sb = b.w >> 14;
		int sa = a.w >> 14;
		int ss = s.w >> 16;
		int st = t.w >> 16;
		int sw = w.w >> 16;
		int sz = (z.w >> 10) & 0x3fffff;
		INT32 sss = 0;
		INT32 sst = 0;

		if (m_rdp->OtherModes.z_source_sel)
		{
			sz = (((UINT32)m_rdp->MiscState.PrimitiveZ) << 6) & 0x3fffff;
			dzpix = m_rdp->MiscState.PrimitiveDZ;
			dzinc = m_rdp->m_span_dz = m_rdp->m_span_dzdy = 0;
		}

		bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored);

		if (x >= clipx1 && x < clipx2 && valid_x)
		{
			m_rdp->lookup_cvmask_derivatives(m_cvg[x], &offx, &offy);

			if (m_rdp->TexPipe.m_start_span)
			{
				if (m_rdp->OtherModes.persp_tex_en)
				{
					m_rdp->TCDiv(ss, st, sw, &sss, &sst);
				}
				else
				{
					m_rdp->TCDivNoPersp(ss, st, sw, &sss, &sst);
				}
			}
			else
			{
				sss = m_rdp->TexPipe.m_precomp_s;
				sst = m_rdp->TexPipe.m_precomp_t;
			}

			m_rdp->TexPipe.LOD1Cycle(&sss, &sst, s.w, t.w, w.w, dsinc, dtinc, dwinc);

			RGBAZCorrectTriangle(offx, offy, &sr, &sg, &sb, &sa, &sz);
			RGBAZClip(sr, sg, sb, sa, &sz);

			m_rdp->TexPipe.Cycle(&m_rdp->Texel0Color, &m_rdp->Texel0Color, sss, sst, tilenum, 0);

			m_rdp->ColorCombiner1Cycle(noisecompute);

			UINT32 curpixel = fb_index + x;
			UINT32 zbcur = zb + curpixel;
			UINT32 zhbcur = zhb + curpixel;

			m_rdp->Framebuffer.Read(curpixel);

			if(m_rdp->ZCompare(zbcur, zhbcur, sz, dzpix))
			{
				m_rdp->GetDitherValues(index, j, &cdith, &adith);

				bool rendered = m_rdp->Blender.Blend1Cycle(&fir, &fig, &fib, cdith, adith, partialreject, bsel0);

                if (rendered)
				{
					m_rdp->Framebuffer.Write(curpixel, fir, fig, fib);
					if (m_rdp->OtherModes.z_update_en)
					{
						m_rdp->ZStore(zbcur, zhbcur, sz);
					}
				}
			}
		}

		r.w += drinc;
		g.w += dginc;
		b.w += dbinc;
		a.w += dainc;
		s.w += dsinc;
		t.w += dtinc;
		w.w += dwinc;
		z.w += dzinc;

		x += xinc;
	}
}

void Span::Draw2Cycle(int index, int tilenum, bool flip)
{
	int clipx1 = m_rdp->GetScissor()->m_xh;
	int clipx2 = m_rdp->GetScissor()->m_xl;

	SpanParam r = m_r;
	SpanParam g = m_g;
	SpanParam b = m_b;
	SpanParam a = m_a;
	SpanParam z = m_z;
	SpanParam s = m_s;
	SpanParam t = m_t;
	SpanParam w = m_w;

	UINT32 zb = m_rdp->MiscState.ZBAddress >> 1;
	UINT32 zhb = zb;
	UINT8 offx = 0, offy = 0;

	INT32 tile2 = (tilenum + 1) & 7;
	INT32 tile1 = tilenum;
	UINT32 prim_tile = tilenum;

	int newtile1 = tile1;
	INT32 news = 0;
	INT32 newt = 0;

	m_rdp->TexPipe.CalculateClampDiffs(tile1);

	bool noisecompute = (m_rdp->ColorInputs.combiner_rgbsub_a_r[0] == &m_rdp->NoiseColor.i.r || m_rdp->ColorInputs.combiner_rgbsub_a_r[1] == &m_rdp->PixelColor.i.r);
	bool partialreject = (m_rdp->ColorInputs.blender2b_a[1] == &m_rdp->InvPixelColor.i.a && m_rdp->ColorInputs.blender1b_a[1] == &m_rdp->PixelColor.i.a);
	bool bsel0 = (m_rdp->ColorInputs.blender2b_a[0] == &m_rdp->MemoryColor.i.a);
	bool bsel1 = (m_rdp->ColorInputs.blender2b_a[1] == &m_rdp->MemoryColor.i.a);

	int dzpix = m_rdp->m_span_dzpix;
	int drinc = flip ? (m_rdp->m_span_dr) : -m_rdp->m_span_dr;
	int dginc = flip ? (m_rdp->m_span_dg) : -m_rdp->m_span_dg;
	int dbinc = flip ? (m_rdp->m_span_db) : -m_rdp->m_span_db;
	int dainc = flip ? (m_rdp->m_span_da) : -m_rdp->m_span_da;
	int dzinc = flip ? (m_rdp->m_span_dz) : -m_rdp->m_span_dz;
	int dsinc = flip ? (m_rdp->m_span_ds) : -m_rdp->m_span_ds;
	int dtinc = flip ? (m_rdp->m_span_dt) : -m_rdp->m_span_dt;
	int dwinc = flip ? (m_rdp->m_span_dw) : -m_rdp->m_span_dw;
	int xinc = flip ? 1 : -1;

	int fb_index = m_rdp->MiscState.FBWidth * index;

	int cdith = 0;
	int adith = 0;

	int xstart = m_lx;
	int xend = m_unscissored_rx;
	int xend_scissored = m_rx;

	int x = xend;

	int length = flip ? (xstart - xend) : (xend - xstart);
	m_rdp->TexPipe.m_start_span = true;
	UINT32 fir, fig, fib;

	//printf( "Span length: %d\n", length);

	for (int j = 0; j <= length; j++)
	{
		int sr = r.w >> 14;
		int sg = g.w >> 14;
		int sb = b.w >> 14;
		int sa = a.w >> 14;
		int ss = s.h.h;
		int st = t.h.h;
		int sw = w.h.h;
		int sz = (z.w >> 10) & 0x3fffff;
		INT32 sss = 0;
		INT32 sst = 0;
		Color c1;
		Color c2;

		if (m_rdp->OtherModes.z_source_sel)
		{
			sz = (((UINT32)m_rdp->MiscState.PrimitiveZ) << 6) & 0x3fffff;
			dzpix = m_rdp->MiscState.PrimitiveDZ;
			dzinc = m_rdp->m_span_dz = m_rdp->m_span_dzdy = 0;
		}

		bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored);

		if (x >= clipx1 && x < clipx2 && valid_x)
		{
			m_rdp->lookup_cvmask_derivatives(m_cvg[x], &offx, &offy);

			if (m_rdp->TexPipe.m_start_span)
			{
				if (m_rdp->OtherModes.persp_tex_en)
				{
					m_rdp->TCDiv(ss, st, sw, &sss, &sst);
				}
				else
				{
					m_rdp->TCDivNoPersp(ss, st, sw, &sss, &sst);
				}
			}
			else
			{
				sss = m_rdp->TexPipe.m_precomp_s;
				sst = m_rdp->TexPipe.m_precomp_t;
			}

			m_rdp->TexPipe.LOD2Cycle(&sss, &sst, s.w, t.w, w.w, dsinc, dtinc, dwinc, prim_tile, &tile1, &tile2);

			news = m_rdp->TexPipe.m_precomp_s;
			newt = m_rdp->TexPipe.m_precomp_t;
			m_rdp->TexPipe.LOD2CycleLimited(&news, &newt, s.w + dsinc, t.w + dtinc, w.w + dwinc, dsinc, dtinc, dwinc, prim_tile, &newtile1);

			RGBAZCorrectTriangle(offx, offy, &sr, &sg, &sb, &sa, &sz);
			RGBAZClip(sr, sg, sb, sa, &sz);

			m_rdp->TexPipe.Cycle(&m_rdp->Texel0Color, &m_rdp->Texel0Color, sss, sst, tile1, 0);
			m_rdp->TexPipe.Cycle(&m_rdp->Texel1Color, &m_rdp->Texel0Color, sss, sst, tile2, 1);

			m_rdp->TexPipe.Cycle(&m_rdp->NextTexelColor, &m_rdp->NextTexelColor, sss, sst, tile2, 1);

			m_rdp->ColorCombiner2Cycle(noisecompute);

			UINT32 curpixel = fb_index + x;
			UINT32 zbcur = zb + curpixel;
			UINT32 zhbcur = zhb + curpixel;

			m_rdp->Framebuffer.Read(curpixel);

			if(m_rdp->ZCompare(zbcur, zhbcur, sz, dzpix))
			{
				m_rdp->GetDitherValues(index, j, &cdith, &adith);

				bool rendered = m_rdp->Blender.Blend2Cycle(&fir, &fig, &fib, cdith, adith, partialreject, bsel0, bsel1);

                if (rendered)
				{
					m_rdp->Framebuffer.Write(curpixel, fir, fig, fib);
					if (m_rdp->OtherModes.z_update_en)
					{
						m_rdp->ZStore(zbcur, zhbcur, sz);
					}
				}
			}
		}

		r.w += drinc;
		g.w += dginc;
		b.w += dbinc;
		a.w += dainc;
		s.w += dsinc;
		t.w += dtinc;
		w.w += dwinc;
		z.w += dzinc;

		x += xinc;
	}
}

void Span::DrawCopy(int index, int tilenum, bool flip)
{
	int clipx1 = m_rdp->GetScissor()->m_xh;
	int clipx2 = m_rdp->GetScissor()->m_xl;

	SpanParam s = m_s;
	SpanParam t = m_t;

	int ds = m_rdp->m_span_ds / 4;
	int dt = m_rdp->m_span_dt / 4;
	int dsinc = flip ? (ds) : -ds;
	int dtinc = flip ? (dt) : -dt;
	int xinc = flip ? 1 : -1;

	int fb_index = m_rdp->MiscState.FBWidth * index;

	int xstart = m_lx;
	int xend = m_unscissored_rx;
	int xend_scissored = m_rx;

	int x = xend;

	int length = flip ? (xstart - xend) : (xend - xstart);

	for (int j = 0; j <= length; j++)
	{
		bool valid_x = (flip) ? (x >= xend_scissored) : (x <= xend_scissored);

		if (x >= clipx1 && x < clipx2 && valid_x)
		{
			INT32 sss = s.h.h;
			INT32 sst = t.h.h;
			m_rdp->TexPipe.Copy(&m_rdp->Texel0Color, sss, sst, tilenum);

			UINT32 curpixel = fb_index + x;
			m_rdp->MiscState.CurrentPixCvg = m_rdp->Texel0Color.i.a ? 7 : 0;
			if ((m_rdp->Texel0Color.i.a != 0) || (!m_rdp->OtherModes.alpha_compare_en))
			{
				m_rdp->Framebuffer.Copy(curpixel, m_rdp->Texel0Color.i.r, m_rdp->Texel0Color.i.g, m_rdp->Texel0Color.i.b);
			}
		}

		s.w += dsinc;
		t.w += dtinc;
		x += xinc;
	}
}

void Span::DrawFill(int index, int tilenum, bool flip)
{
	int clipx1 = m_rdp->GetScissor()->m_xh;
	int clipx2 = m_rdp->GetScissor()->m_xl;

	int xinc = flip ? 1 : -1;

	int fb_index = m_rdp->MiscState.FBWidth * index;

	int xstart = m_lx;
	int xend_scissored = m_rx;

	int x = xend_scissored;

	int length = flip ? (xstart - xend_scissored) : (xend_scissored - xstart);

	for (int j = 0; j <= length; j++)
	{
		if (x >= clipx1 && x < clipx2)
		{
			UINT32 curpixel = fb_index + x;
			m_rdp->Framebuffer.Fill(curpixel);
		}

		x += xinc;
	}
}

}

}
