
#define SQUARE(A) ((A)*(A))

/*---------------------------------------------------------------------------
 *	Import Headers
\*--------------------------------------------------------------------------*/

#include <gtemac.h>
#include <inline_c.h>
#include "typedefs.h"
#include "3d_gen.h"
#include "maths.h"
#include "..\game\room.h"
#include "..\game\draw.h"

extern ITEM_INFO *lara_item;
extern long	wibble;

typedef struct
{
	long		x;
	long		y;
	long		z;

	unsigned	short	falloff;	// 0 - 31. (1/8 block per value).
	unsigned	char	used;
	unsigned	char	pad1[1];

	unsigned	char	on;
	unsigned	char r;
	unsigned	char g;
	unsigned	char b;

} DYNAMIC;

/*---------------------------------------------------------------------------
 *	Constants
\*--------------------------------------------------------------------------*/

#define WIBBLE_SIZE 64

#define SUNSET_TIME (60*60*1)	// in frames (60 per second)

enum lighting_type {
	LIGHT_NONE,
	LIGHT_FLICKER,
	LIGHT_PULSE,
	LIGHT_SUNSET,
	NUMBER_LIGHT_TYPES
};

/*---------------------------------------------------------------------------
 *	Externals
\*--------------------------------------------------------------------------*/

extern	DYNAMIC	dynamics[];
extern	unsigned	short	LOffset[];
extern	unsigned	char		LTab[];

/*---------------------------------------------------------------------------
 *	Globals
\*--------------------------------------------------------------------------*/

MATRIX3D CamGTE;
MATRIX3D LightPos;
MATRIX3D LightCol;
SVECTOR3D LPos[3];

/*---------------------------------------------------------------------------
 *	Locals
\*--------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
 *	Public Functions
\*--------------------------------------------------------------------------*/

void SetLightMatrices()
{
#if 0
	iLoadMatrix(&CamGTE);

	gte_ldlv0(&LPos[0]);
	gte_llir();

	gte_stsv(&LightPos.m00);
	gte_ldlv0(&LPos[1]);
	gte_llir();

	gte_stsv(&LightPos.m10);
	gte_ldlv0(&LPos[2]);
	gte_llir();

	gte_stsv(&LightPos.m20);
#else
	mPushMatrix();

	ApplyMatrixSV((MATRIX *)&CamGTE, (SVECTOR *)&LPos[0], (SVECTOR *)&LPos[0]);
	LightPos.m00 = LPos[0].x;
	LightPos.m01 = LPos[0].y;
	LightPos.m02 = LPos[0].z;

	ApplyMatrixSV((MATRIX *)&CamGTE, (SVECTOR *)&LPos[1], (SVECTOR *)&LPos[1]);
	LightPos.m10 = LPos[1].x;
	LightPos.m11 = LPos[1].y;
	LightPos.m12 = LPos[1].z;

	ApplyMatrixSV((MATRIX *)&CamGTE, (SVECTOR *)&LPos[2], (SVECTOR *)&LPos[2]);
	LightPos.m20 = LPos[2].x;
	LightPos.m21 = LPos[2].y;
	LightPos.m22 = LPos[2].z;

	mPopMatrix();
#endif

	SetColorMatrix((MATRIX *)&LightCol);
}

void S_CalculateLight(sint32 x, sint32 y, sint32 z, sint16 room_number, ITEM_LIGHT *il)
{
	LIGHT_INFO *sl=NULL, sunlight;	// ptr to target sun colour, dummy bulb

	int i;
	ROOM_INFO *r;
	ITEM_LIGHT dummy;
	sint32 brightest;
	DYNAMIC *dl, *dl2=NULL;
	PHD_VECTOR ls, lc;
	LIGHT_INFO *light, *l=NULL;
	uint16 ambience;
	char sun;

	if (il == NULL)
	{
		il = &dummy;
		il->init = 0;
	}

	LightPos.m00 = LightPos.m01 = LightPos.m02 =
	LightPos.m10 = LightPos.m11 = LightPos.m12 =
	LightPos.m20 = LightPos.m21 = LightPos.m22 = 0;

	LightCol.m00 = LightCol.m01 = LightCol.m02 =
	LightCol.m10 = LightCol.m11 = LightCol.m12 =
	LightCol.m20 = LightCol.m21 = LightCol.m22 = 0;

/* -------- determine brightest room light */

	sun = 0;
	brightest = -1;

	r = &room[room_number];

	ambience = (r->ambient >> 5) + 16;

	for ( i=r->num_lights,light=r->light ; i>0 ; i--,light++ )
	{
		sint32 distance, shade;

	/* -------- process sun bulb */

		if (light->type)
		{
			sun = 1;
			sl = light;
		}

	/* -------- process omni bulb */

		else
		{
			lc.x = light->x - x;
			lc.y = light->y - y;
			lc.z = light->z - z;

			distance = mSqrt(SQUARE(lc.x) + SQUARE(lc.y) + SQUARE(lc.z));

			if (distance > light->l.spot.falloff)
				continue;

			shade = light->l.spot.intensity - (((light->l.spot.intensity * distance) / light->l.spot.falloff));

			ambience += (shade >> (5+2));

			if (shade > brightest)
			{
				brightest = shade;
				l = light;
				ls = lc;
			}
		}
	}

/* -------- interpolate sun bulb */

	if ((!sun) && (il->init & 1))
	{
		sunlight.l.sun.nx = il->sun.x;
		sunlight.l.sun.ny = il->sun.y;
		sunlight.l.sun.nz = il->sun.z;

		sunlight.r = sunlight.g = sunlight.b = r->ambient >> 5;

		sl = &sunlight;
		sun = 1;
	}

	if (sun)
	{
		if (il->init & 1)
		{
			il->sun.x += (sl->l.sun.nx - il->sun.x) >> 3;
			il->sun.y += (sl->l.sun.ny - il->sun.y) >> 3;
			il->sun.z += (sl->l.sun.nz - il->sun.z) >> 3;

			il->sunr += (sl->r - il->sunr) >> 3;
			il->sung += (sl->g - il->sung) >> 3;
			il->sunb += (sl->b - il->sunb) >> 3;
		}
		else
		{
			il->sun.x = sl->l.sun.nx;
			il->sun.y = sl->l.sun.ny;
			il->sun.z = sl->l.sun.nz;

			il->sunr = sl->r;
			il->sung = sl->g;
			il->sunb = sl->b;
			il->init |= 1;
		}

		LPos[0].x = il->sun.x;
		LPos[0].y = il->sun.y;
		LPos[0].z = il->sun.z;

		LightCol.m00 = il->sunr << 4;
		LightCol.m10 = il->sung << 4;
		LightCol.m20 = il->sunb << 4;
	}

/* -------- interpolate omni bulb */

	if ((brightest == -1) && (il->init & 2))
	{
		ls.x	= il->bulb.x;
		ls.y	= il->bulb.y;
		ls.z	= il->bulb.z;

		sunlight.r = sunlight.g = sunlight.b = r->ambient >> 5;
		l = &sunlight;

		brightest = 8191;
	}

	if (brightest != -1)
	{
		phd_NormaliseVector(ls.x, ls.y, ls.z, (sint32 *)&ls);

		if (il->init & 2)
		{
			il->bulb.x += (ls.x - il->bulb.x) >> 3;
			il->bulb.y += (ls.y - il->bulb.y) >> 3;
			il->bulb.z += (ls.z - il->bulb.z) >> 3;

			il->bulbr += (((l->r * brightest) >> 13) - il->bulbr) >> 3;
			il->bulbg += (((l->g * brightest) >> 13) - il->bulbg) >> 3;
			il->bulbb += (((l->b * brightest) >> 13) - il->bulbb) >> 3;
		}
		else
		{
			il->bulb.x = ls.x;
			il->bulb.y = ls.y;
			il->bulb.z = ls.z;

			il->bulbr = (l->r * brightest) >> 13;
			il->bulbg = (l->g * brightest) >> 13;
			il->bulbb = (l->b * brightest) >> 13;
			il->init |= 2;
		}

		LPos[1].x = il->bulb.x >> 2;
		LPos[1].y = il->bulb.y >> 2;
		LPos[1].z = il->bulb.z >> 2;

		LightCol.m01 = il->bulbr << 4;
		LightCol.m11 = il->bulbg << 4;
		LightCol.m21 = il->bulbb << 4;
	}

/* -------- determine brightest dynamic light */

	brightest = -1;

	for ( i=0,dl=dynamics ; i<number_dynamics ; i++,dl++ )
	{
		sint32 distance, shade;

		lc.x = dl->x - x;
		lc.y = dl->y - y;
		lc.z = dl->z - z;

		if ((abs(lc.x) > 8192)
		||  (abs(lc.y) > 8192)
		||  (abs(lc.z) > 8192))
			continue;

		distance = mSqrt(SQUARE(lc.x) + SQUARE(lc.y) + SQUARE(lc.z));

		if (distance > (dl->falloff >> 1))
			continue;

		shade = 8191 - (((8191 * distance) / (dl->falloff >> 1)));

		ambience += (shade >> 8);

		if (shade > brightest)
		{
			brightest = shade;
			ls = lc;
			dl2 = dl;
		}
	}

	if (brightest != -1)
	{
		phd_NormaliseVector(ls.x, ls.y, ls.z, (sint32 *)&ls);

#if 1
		if (il->init & 4)
		{
			il->dynamic.x += (ls.x - il->dynamic.x) >> 1;
			il->dynamic.y += (ls.y - il->dynamic.y) >> 1;
			il->dynamic.z += (ls.z - il->dynamic.z) >> 1;

			il->dynamicr += (((dl2->r * brightest) >> (13-3)) - il->dynamicr) >> 1;
			il->dynamicg += (((dl2->g * brightest) >> (13-3)) - il->dynamicg) >> 1;
			il->dynamicb += (((dl2->b * brightest) >> (13-3)) - il->dynamicb) >> 1;
		}
		else
#endif
		{
			il->dynamic.x = ls.x;
			il->dynamic.y = ls.y;
			il->dynamic.z = ls.z;

			il->dynamicr = (dl2->r * brightest) >> (13-3);
			il->dynamicg = (dl2->g * brightest) >> (13-3);
			il->dynamicb = (dl2->b * brightest) >> (13-3);
			il->init |= 4;
		}

		LPos[2].x = il->dynamic.x >> 2;
		LPos[2].y = il->dynamic.y >> 2;
		LPos[2].z = il->dynamic.z >> 2;

		LightCol.m02 = il->dynamicr << 6;
		LightCol.m12 = il->dynamicg << 6;
		LightCol.m22 = il->dynamicb << 6;
	}

/* -------- determine ambience */

	if (ambience > 255)
		ambience = 255;

	if (il->init & 8)
		il->ambient += (ambience - il->ambient) >> 3;
	else
	{
		il->ambient = ambience;
		il->init |= 8;
	}

	SetBackColor(il->ambient, il->ambient, il->ambient);

	SetLightMatrices();
}

void	S_CalculateStaticLight(sint16 adder)
{
	CVECTOR c;

//	c.r = c.g = c.b = (8192 - adder) >> 5;

	c.r = (adder & 0x1f) << 3;
	c.g = (adder & 0x3e0) >> 2;
	c.b = (adder & 0x7c00) >> 7;

	gte_ldrgb(&c);
}

void S_CalculateStaticMeshLight(int x, int y, int z, int shade, int shadeB, ROOM_INFO *room)
{
	int i;
	DYNAMIC *dl;
	sint16 lr, lg, lb;

	lr = lg = lb = 0;

	for ( i=0,dl=dynamics ; i<number_dynamics ; i++,dl++ )
	{
		sint32 dx, dy, dz;
		sint32 distance, intensity;

		dx = x - dl->x;
		dy = y - dl->y;
		dz = z - dl->z;

		if ((abs(dx) > 8192)
		||  (abs(dy) > 8192)
		||  (abs(dz) > 8192))
			continue;

		distance = mSqrt(SQUARE(dx) + SQUARE(dy) + SQUARE(dz));

		if (distance > (dl->falloff >> 1))
			continue;

		intensity = 8191 - (((8191 * distance) / (dl->falloff >> 1)));

		lr += (dl->r * intensity) >> (13-3);
		lg += (dl->g * intensity) >> (13-3);
		lb += (dl->b * intensity) >> (13-3);
	}

	if ((lr+lg+lb))
	{
		CVECTOR c;

		lr += ((shade &   0x1f) << 3);
		lg += ((shade &  0x3e0) >> 2);
		lb += ((shade & 0x7c00) >> 7);

		if (lr > 255)
			lr = 255;
		if (lg > 255)
			lg = 255;
		if (lb > 255)
			lb = 255;

		c.r = lr;
		c.g = lg;
		c.b = lb;
		gte_ldrgb(&c);
	}
	else
		S_CalculateStaticLight(shade);
}

//void	S_CalculateStaticMeshLight(int x, int y, int z, int shade, int shadeB, ROOM_INFO *r)
//{
//	S_CalculateStaticLight(shade);
//}


#if 0
void	S_CalculateStaticLight(sint16 adder)
{
	sint32 zdist;

	ls_adder = adder - (16*256);
	zdist = *(phd_mxptr+M23)>>W2V_SHIFT;
	if ( zdist>DPQ_START )
		ls_adder += zdist - DPQ_START;
}

void	S_CalculateStaticMeshLight(int x, int y, int z, int shade, int shadeB, ROOM_INFO *r)
{
	sint32 i, distance, falloff;
	PHD_VECTOR lc;
	LIGHT_INFO *dl;

	/* Do room effect lighting first */
	if (r->lighting != LIGHT_NONE)
		shade += (shadeB - shade) * light_level[r->lighting] / (WIBBLE_SIZE-1);

	/* Add the effects of dynamic lights. Note that the intensity and falloff
	are stored as powers of 2, so that almost everything can be calced with bit shifts and it looks really gyooooooooooood!*/
/*
	for (i=0, dl=dynamic; i<number_dynamics; i++, dl++)
	{
		falloff = 1<<dl->falloff;

		lc.x = x - dl->x;
		if (lc.x < -falloff || lc.x > falloff)
			continue;

		lc.y = y - dl->y;
		if (lc.y < -falloff || lc.y > falloff)
			continue;

		lc.z = z - dl->z;
		if (lc.z < -falloff || lc.z > falloff)
			continue;

		distance = SQUARE(lc.x) + SQUARE(lc.y) + SQUARE(lc.z);
		if (distance > (1 << (dl->falloff<<1)))
			continue;

		// Effects of dynamic lights are cummulative (how they effect room too)
		shade -= (1<<dl->intensity) - (distance >> ((dl->falloff<<1) - dl->intensity));
		if (shade < 0)
		{
			shade = 0;
			break;		// as bright as we can go, so exit
		}
	}
*/

	S_CalculateStaticLight(shade);
}

#endif
