// Post.c

#include "ult_64.h"

#include "main.h"
#include "define.h"
#include "ml.h"

#include "misc.h"
#include "view.h"
#include "fastcode.h"
#include "post.h"


typedef struct SPost
{
	char
		Type[4],
		Game[4],
		Platform[4];
	s32
		Version,
		NumMaterials;
	s16
		VertSize,
		NumVerts;
	s32
		VertexOffset,
		IndexOffset,
		NumFlex,
		FlexOffset;
	vector16
		Base;
} SPost;

typedef struct SGlass
{
	u8
		Texture[32*16];
	VertexN64
		Vertices[4];
} SGlass;


extern vector16
	WorkVerts[];


matrix
	PostMtxs[2];


static SPost
	*Post;

static u8
	*Reflect;

static SMaterial
	*Materials;

static SGlass
	*Glass;

static Gfx
	*ReflectList;

static VertexN64
	*VtxLists[2][2],
	*GlassVtxs[2][2];

static s32
	Angles[2];


/******************************************************************************/

static void MakeReflect( u8 *object )
{
	VertexN64
		*vtx;
	s16
		*ptr,
		level,
		count,
		command;

	ptr = (s16 *)&object[6*4];
	vtx = (VertexN64 *)&object[*(s32 *)&object[5*4]];

	while (*ptr)
	{
		command = ptr[0];
		count = ptr[1];
		ptr += 2;

		switch (command)
		{
			case CMD_VERTICES:
				while (count)
				{
					level = 128 + (vtx->y >> 3);
					if (level < 0)
						level = 0;
					vtx->a = (u8)level;

					vtx->y -= Post->Base.y;
					vtx->z -= Post->Base.z;

					vtx++;
					count--;
				}
				break;

			case CMD_FACES:
				ptr += 3 * count;
				break;
		}
	}
}


static void Intersect( VertexN64 *a, VertexN64 *b, Plane *p, VertexN64 *c )
{
	s32
		i,
		j,
		k,
		x,
		y,
		z,
		d,
		t;

	i = (b->x - a->x);
	j = (b->y - a->y);
	k = (b->z - a->z);


	d = (((s32)p->x * i) +
			 ((s32)p->y * j) +
			 ((s32)p->z * k)) >> 14;

	if (d == 0)
	{
		*c = *a;
		return;
	}

	t = (((s32)p->x * (s32)a->x) +
			 ((s32)p->y * (s32)a->y) +
			 ((s32)p->z * (s32)a->z)) >> 14;

	t += p->d;
	t <<= 14;
	t /= d;
	t = -t;

	c->x = a->x + (s16)((t * i) >> 14);
	c->y = a->y + (s16)((t * j) >> 14);
	c->z = a->z + (s16)((t * k) >> 14);

	c->u = a->u + (s16)((t * (s32)(b->u - a->u)) >> 14);
	c->v = a->v + (s16)((t * (s32)(b->v - a->v)) >> 14);

	*(u32 *)&c->r = *(u32 *)&a->r;
}


static s32 PointInFrontOfPlane( VertexN64 *v, Plane *p )
{
	s32
		d;

	d = (((s32)v->x * (s32)p->x) +
			 ((s32)v->y * (s32)p->y) +
			 ((s32)v->z * (s32)p->z)) >> 14;

	d += p->d;

	if (d < 0)
		return( 0 );

	return( -1 );
}


static s32 ClipPlane( VertexN64 *in, s32 count, Plane *plane, VertexN64 *out )
{
	s32
		i,
		j,
		n,
		c;

	i = 0;
	j = 1;
	c = 0;

	for (n=0; n<count; n++)
	{
		if (PointInFrontOfPlane( &in[i], plane ))
		{
			*out++ = in[i];
			c++;

			if (!PointInFrontOfPlane( &in[j], plane ))
			{
				Intersect( &in[j], &in[i], plane, out );
				out++;
				c++;
			}
		}
		else
		{
			if (PointInFrontOfPlane( &in[j], plane ))
			{
				Intersect( &in[j], &in[i], plane, out );
				out++;
				c++;
			}
		}

		i++;

		j++;
		if (j >= count)
			j = 0;
	}

	return( c );
}


Gfx *Post_DrawMain( Gfx *gp, s32 which, s32 db, dyn *d )
{
	SMaterial
		*mat;
	VertexN64
		*vtx;
	vector16
		*verts;
	matrix
		mtx;
	s8
		*ptr,
		count,
		command;
	s16
		*iptr;
	s32
		i,
		tex = 0;

	gDPPipeSync( gp++ );
	gDPSetRenderMode( gp++, G_RM_RA_ZB_OPA_SURF, G_RM_RA_ZB_OPA_SURF2 );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gSPMatrix( gp++, &d->PostMats[which], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );


	ptr = (s8 *)&Post[1];
	vtx = VtxLists[which][db];
	iptr = (s16 *)((u32)Post + Post->IndexOffset);


	verts = (vector16 *)((u32)Post + Post->FlexOffset);
	count = Post->NumFlex;

	if (Angles[which])
	{
		MakeMatPRH( &mtx, -Angles[which], 0, 0 );

		for (i=0; i<count; i++)
			Vec16MulMat( &verts[i], &mtx, &WorkVerts[i] );
	}
	else
		Copy64s( verts, WorkVerts, count );


	while (*ptr)
	{
		command = ptr[0];
		count = ptr[1];
		ptr += 2;

		switch (command)
		{
			case CMD_TEXTURE:
				if (count < 0)
				{
					if (tex != 1)
					{
						tex = 1;
						gDPPipeSync( gp++ );
						gSPTexture( gp++, 0x8000, 0x8000, 0, 0, G_OFF );
						gDPSetCombineMode( gp++, G_CC_SHADE, G_CC_SHADE );
					}
				}
				else
				{
					mat = &Materials[count];

					if (tex != 2)
					{
						tex = 2;
						gDPPipeSync( gp++ );
						gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
						gDPSetCombineMode( gp++, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
					}

					switch (mat->Flags.Format)
					{
						case FORMAT_CI4:
							if (mat->Texture)
							{
								gDPLoadTextureBlock_4b( gp++,
																				mat->Texture,
																				G_IM_FMT_CI,
																				mat->Width, mat->Height,
																				mat->Flags.PalNum,
																				mat->Flags.Xflags,
																				mat->Flags.Yflags,
																				mat->Flags.Xmask,
																				mat->Flags.Ymask,
																				G_TX_NOLOD, G_TX_NOLOD );
								gDPSetTextureLUT( gp++, G_TT_RGBA16 );
							}
							if (mat->Palette)
							{
								gDPLoadTLUT_pal16( gp++, 0, mat->Palette );
							}
							break;

						case FORMAT_CI8:
							if (mat->Texture)
							{
								gDPLoadTextureBlock( gp++,
																		 mat->Texture,
																		 G_IM_FMT_CI, G_IM_SIZ_8b,
																		 mat->Width, mat->Height,
																		 0,
																		 mat->Flags.Xflags,
																		 mat->Flags.Yflags,
																		 mat->Flags.Xmask,
																		 mat->Flags.Ymask,
																		 G_TX_NOLOD, G_TX_NOLOD );
								gDPSetTextureLUT( gp++, G_TT_RGBA16 );
							}
							if (mat->Palette)
							{
								gDPLoadTLUT_pal256( gp++, mat->Palette );
							}
							break;
					}
				}
				break;

			case CMD_VERTICES:
				gSPVertex( gp++, vtx, count, 0 );
				while (count)
				{
					i = *iptr++;
					if (i >= 0)
					{
						vtx->x = WorkVerts[i].x;
						vtx->y = WorkVerts[i].y;
						vtx->z = WorkVerts[i].z;
					}

					count--;
					vtx++;
				}
				break;

			case CMD_FACES:
				while (count >= 2)
				{
					gSP2Triangles( gp++,
												 ptr[0], ptr[1], ptr[2], 0,
												 ptr[3], ptr[4], ptr[5], 0 );
					ptr += 2*3;
					count -= 2;
				}
				if (count > 0)
				{
					gSP1Triangle( gp++, ptr[0], ptr[1], ptr[2], 0 );
					ptr += 3;
				}
				break;
		}
	}


	return( gp );
}


Gfx *Post_DrawReflect( Gfx *gp, s32 which, s32 db, dyn *d )
{
	gSPMatrix( gp++, &d->PostMats[which+2], G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH );
	gSPDisplayList( gp++, ReflectList );


	return( gp );
}


Gfx *Post_DrawGlass( Gfx *gp, s32 which, s32 db, dyn *d )
{
	VertexN64
		*vtx,
		*work,
		out[16];
	vector16
		v;
	s32
		i,
		count;

	vtx = Glass->Vertices;
	work = &GlassVtxs[which][db][0];

	for (i=0; i<4; i++)
	{
		v.x = vtx[i].x;
		v.y = vtx[i].y - Post->Base.y;
		v.z = vtx[i].z - Post->Base.z;

		if (which)
		{
			Vec16MulMat( &v, &PostMtxs[1], &v );
			work[i].z = v.z - Post->Base.z - (GLASS_Y>>10) - 96;
		}
		else
		{
			Vec16MulMat( &v, &PostMtxs[0], &v );
			work[i].z = v.z + Post->Base.z + (GLASS_Y>>10) + 96;
		}

		work[i].x = v.x + Post->Base.x;
		work[i].y = v.y + Post->Base.y;

		*(u32 *)&work[i].u = *(u32 *)&vtx[i].u;
	}

	count = ClipPlane( work, 4, &View->Front, out );
	if (!count)	return( gp );

	count = ClipPlane( out, count, &View->Left, work );
	if (!count)	return( gp );

	count = ClipPlane( work, count, &View->Right, out );
	if (!count)	return( gp );

	count = ClipPlane( out, count, &View->Top, work );
	if (!count)	return( gp );

	count = ClipPlane( work, count, &View->Bot, out );
	if (!count)	return( gp );

	memcpy( work, out, count * sizeof( VertexN64 ) );


	gDPPipeSync( gp++ );
	gDPSetCombineMode( gp++, G_CC_DECALRGBA, G_CC_DECALRGBA );
	gSPTexture( gp++, 0x8000, 0x8000, 0, G_TX_RENDERTILE, G_ON );
	gDPLoadTextureBlock( gp++,
											 Glass->Texture,
											 G_IM_FMT_IA, G_IM_SIZ_8b,
											 32, 16,
											 0,
											 G_TX_CLAMP, G_TX_CLAMP,
											 5, 4,
											 G_TX_NOLOD, G_TX_NOLOD );
	gDPSetTextureLUT( gp++, G_TT_NONE );

	gSPVertex( gp++, work, count, 0 );

	for (i=1; i<(count-1); i++)
	{
		gSP1Triangle( gp++, 0, i, i+1, 0 );
	}

	return( gp );
}


void Post_Update()
{
	static u8
		bits[10] =
		{
			0x77, // 1110111
			0x24, // 0100100
			0x5d, // 1011101
			0x6d, // 1101101
			0x2e, // 0101110
			0x6b, // 1101011
			0x7b, // 1111011
			0x25, // 0100101
			0x7f, // 1111111
			0x6f, // 1101111
		};
	SMaterial
		*mat = Materials;
	s32
		i,
		secs,
		mask,
		angle;

	if (ball.flip_yco > 0)
		angle = LFPAtn( 3610, swingHeight );
	else
		angle = 0;

	Angles[0] = angle;
	MakeMatPRH( &PostMtxs[0], angle, 0, 0 );


	if (ball.flip_yco < 0)
		angle = LFPAtn( 3610, swingHeight );
	else
		angle = 0;

	Angles[1] = angle;
	MakeMatPRH( &PostMtxs[1], angle, 0, 32768 );


	// Shot clock...

	if (C3PT || SEC24_CLOCK)
	{
		if (C3PT)
			secs = time_left;
		else
			secs = shot_clock;

		secs = (secs + ONE_SEC - 1) / ONE_SEC;
		if (secs < 0)
			secs = 0;
	}
	else
		secs = -1;


	if (secs < 0)
		mask = 0;
	else
		mask = bits[secs / 10];

	for (i=0; i<7; i++)
	{
		if (mask & 1)
		{
			mat->Palette[64+i].r = 31;
			mat->Palette[64+i].g = 7;
			mat->Palette[64+i].b = 0;
		}
		else
		{
			mat->Palette[64+i].r = 8;
			mat->Palette[64+i].g = 2;
			mat->Palette[64+i].b = 0;
		}

		mask >>= 1;
	}


	if (secs < 0)
		mask = 0;
	else
		mask = bits[secs % 10];

	for (i=0; i<7; i++)
	{
		if (mask & 1)
		{
			mat->Palette[71+i].r = 31;
			mat->Palette[71+i].g = 7;
			mat->Palette[71+i].b = 0;
		}
		else
		{
			mat->Palette[71+i].r = 8;
			mat->Palette[71+i].g = 2;
			mat->Palette[71+i].b = 0;
		}

		mask >>= 1;
	}
}


void Post_Setup( dyn *d )
{
	matrix
		mtx;

	MatToN64( &PostMtxs[0], &d->PostMats[0] );
	SetTransN64( &d->PostMats[0],
							 Post->Base.x,
							 Post->Base.y,
							 Post->Base.z + (GLASS_Y>>10) + 96 );

	MatToN64( &PostMtxs[1], &d->PostMats[1] );
	SetTransN64( &d->PostMats[1],
							 Post->Base.x,
							 Post->Base.y,
							 -(Post->Base.z + (GLASS_Y>>10) + 96) );

	MakeMatPRH( &mtx, 0, 0, 0 );
	MatToN64( &mtx, &d->PostMats[2] );
	SetTransN64( &d->PostMats[2],
							 Post->Base.x,
							 Post->Base.y,
							 Post->Base.z + (GLASS_Y>>10) + 96 );

	MakeMatPRH( &mtx, 0, 0, 32768 );
	MatToN64( &mtx, &d->PostMats[3] );
	SetTransN64( &d->PostMats[3],
							 Post->Base.x,
							 Post->Base.y,
							 -(Post->Base.z + (GLASS_Y>>10) + 96) );
}


vector16 *Post_GetBase()
{
	return( &Post->Base );
}


void Post_Init( void *workspace )
{
	RGBA16
		*pal;
	Gfx
		*gp;
	u8
		*ptr;
	s32
		team,
		size;

	swingHeight = 0;


	Post = AllocAndLoadFile( "Post.DAT", MEM_ANY );
	Materials = AllocAndLoadFile( "Post.MTL", MEM_ANY );
	FixUpMatList( Materials, 3 );

	pal = AllocAndLoadFile( "PostCols.PAL", MEM_TMP );

	team = Stadium_GetNum( pcl__gMatch->asb__mTeam[0] );

	Materials->Palette[1] = pal[(team * 2) + 0];
	Materials->Palette[2] = pal[(team * 2) + 1];

	MemFree( pal );


	Reflect = AllocAndLoadFile( "Reflect.OBJ", MEM_ANY );
	MakeReflect( Reflect );

	gDPSetCombineMode( &ColModeCombine, G_CC_SHADE, G_CC_SHADE );
	gDPSetCombineMode( &TexModeCombine, G_CC_MODULATERGBA, G_CC_MODULATERGBA );
	gp = DrawObject( workspace, Reflect, Materials );
	gSPEndDisplayList( gp++ );
	size = (s32)gp - (s32)workspace;
	ReflectList = MemMalloc( size, MEM_ANY );
	gp = DrawObject( ReflectList, Reflect, Materials );
	gSPEndDisplayList( gp++ );


	ptr = MemMalloc( 3 * Post->VertSize, MEM_ANY );
	VtxLists[0][0] = (VertexN64 *)((u32)Post + Post->VertexOffset);
	VtxLists[0][1] = (VertexN64 *)&ptr[0 * Post->VertSize];
	VtxLists[1][0] = (VertexN64 *)&ptr[1 * Post->VertSize];
	VtxLists[1][1] = (VertexN64 *)&ptr[2 * Post->VertSize];
	memcpy( VtxLists[0][1], VtxLists[0][0], Post->VertSize );
	memcpy( VtxLists[1][0], VtxLists[0][0], Post->VertSize );
	memcpy( VtxLists[1][1], VtxLists[0][0], Post->VertSize );


	Glass = AllocAndLoadFile( "Glass.DAT", MEM_ANY );

	ptr = MemMalloc( 2*2*16*sizeof( VertexN64 ), MEM_ANY );
	GlassVtxs[0][0] = (VertexN64 *)&ptr[0 * 16 * sizeof( VertexN64 )];
	GlassVtxs[0][1] = (VertexN64 *)&ptr[1 * 16 * sizeof( VertexN64 )];
	GlassVtxs[1][0] = (VertexN64 *)&ptr[2 * 16 * sizeof( VertexN64 )];
	GlassVtxs[1][1] = (VertexN64 *)&ptr[3 * 16 * sizeof( VertexN64 )];
}


void Post_Deinit()
{
	MemFree( GlassVtxs[0][0] );
	MemFree( Glass );

	MemFree( VtxLists[0][1] );
	MemFree( ReflectList );
	MemFree( Reflect );
	MemFree( Materials );
	MemFree( Post );
}
