/*
 * $Id$
 *
 * $Date$
 * $Revision$
 *
 * (C) 1999 by Thomas and Hans-Jörg Frieden
 * All rights reserved
 *
 * This file is part of the MiniGL library project
 * See the file Licence.txt for more details
 *
 */

#include "displaylists.h"
#include "sysinc.h"
#include <stdio.h>
#include <proto/timer.h>
#include "mgl/gl.h"
#include <math.h>

#ifndef M_PI
#    define  M_PI  3.14159265358979323846
#endif

extern float CLAMPF(float a);

#ifdef MGL_USE_LIGHTING

/* These are helpers for computing the lighting formulas */

static inline GLfloat max_dot(MGLDirection *d1, MGLDirection *d2)
{
	GLfloat res = d1->x * d2->x + d1->y * d2->y + d1->z * d2->z;

	if (res > 0.0)
		return res;
	else
		return 0.0;
}

#define COLOR_COPY(_r, _c1)			\
do									\
{									\
	_r.r = _c1.r;					\
	_r.g = _c1.g;					\
	_r.b = _c1.b;					\
} while (0)

#define COLOR_MULT(_r, _c1, _c2)	\
do 									\
{									\
	_r.r = _c1.r * _c2.r;			\
	_r.g = _c1.g * _c2.g;			\
	_r.b = _c1.b * _c2.b;			\
} while (0)

#define COLOR_ADD(_r, _c1, _c2)		\
do									\
{									\
	_r.r = _c1.r + _c2.r;			\
	_r.g = _c1.g + _c2.g;			\
	_r.b = _c1.b + _c2.b;			\
} while (0)

#define COLOR_MULT_SCALAR(_r, _s, _c1)	\
do										\
{										\
	_r.r = _c1.r * _s;					\
	_r.g = _c1.g * _s;					\
	_r.b = _c1.b * _s;					\
} while (0)

#define COLOR_MUL_ACCUM(_r, _c1, _c2)	\
do										\
{										\
	_r.r += _c1.r * _c2.r;				\
	_r.g += _c1.g * _c2.g;				\
	_r.b += _c1.b * _c2.b;				\
} while (0)

#define COLOR_ADD_SCALED(_r, _s, _c)	\
do										\
{										\
	GLfloat s = _s;						\
	_r.r += s * _c.r;					\
	_r.g += s * _c.g;					\
	_r.b += s * _c.b;					\
} while (0)

#define COLOR_PRINT(_r)				\
do										\
{										\
	kprintf("%s: %f, %f, %f\n", 		\
		#_r,							\
		_r.r, _r.g, _r.b);				\
} while (0)

/* 3-component vector math */
#define VEC_NORM3(v)													\
{																		\
    GLfloat recM = fast_reciprocal_sqrt(v.x*v.x + v.y*v.y + v.z*v.z);	\
	v.x *= recM;														\
	v.y *= recM;														\
	v.z *= recM;														\
}

#define VEC_LEN3(v) fast_sqrt(v.x*v.x + v.y*v.y + v.z*v.z)

#define VEC_REC_LEN3(v) fast_reciprocal_sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
	
#define VEC_SUB3(v, a, b)							\
{													\
    v.x = a.x - b.x;								\
    v.y = a.y - b.y;								\
    v.z = a.z - b.z;								\
}

#define VEC_ADD3(v, a, b)							\
{													\
    v.x = a.x + b.x;								\
    v.y = a.y + b.y;								\
    v.z = a.z + b.z;								\
}

#define VEC_SCALE3(v, _s)							\
{													\
	GLfloat s = _s;									\
	v.x *= s;										\
	v.y *= s;										\
	v.z *= s;										\
}

/* 4-component vector math */
#define VEC_NORM4(v)													\
{																		\
	GLfloat recM;														\
	v.x /= v.w;															\
	v.y /= v.w;															\
	v.z /= v.w;															\
	v.w = 1.0;															\
    recM = fast_reciprocal_sqrt(v.x*v.x + v.y*v.y + v.z*v.z);			\
	v.x *= recM;														\
	v.y *= recM;														\
	v.z *= recM;														\
}

#define VEC_SUB4(v, a, b)							\
{													\
    v.x = a.x - b.x;								\
    v.y = a.y - b.y;								\
    v.z = a.z - b.z;								\
    v.w	= a.w - b.w;								\
}

#define VEC_ADD4(v, a, b)							\
{													\
    v.x = a.x + b.x;								\
    v.y = a.y + b.y;								\
    v.z = a.z + b.z;								\
    v.w = a.w + b.w;								\
}

#define VEC4_VEC3(v)								\
{													\
	v.x /= v.w;										\
	v.y /= v.w;										\
	v.z /= v.w;										\
	v.w = 1.0;										\
}

void light_UpdateAmbient(GLcontext context, GLmaterial *mat)
{
	int i;
	
	if(!context->enable.Lighting)
	{
		return;
	}
	
	for (i = 0; i <= context->lighting.MaxLight; i++)
	{
		if (context->enable.Light[i])
		{
				COLOR_MULT(context->lighting.Light[i].Acm_by_Acli[mat->Index],
						   mat->Ambient,
						   context->lighting.Light[i].Ambient);
		}
	}
}

void light_UpdateDiffuse(GLcontext context, GLmaterial *mat)
{
	int i;
	
	if(!context->enable.Lighting)
	{
		return;
	}
	
	for (i = 0; i <= context->lighting.MaxLight; i++)
	{
		if (context->enable.Light[i])
		{
				COLOR_MULT(context->lighting.Light[i].Dcm_by_Dcli[mat->Index],
						   mat->Diffuse,
						   context->lighting.Light[i].Diffuse);
		}
	}
}

void light_UpdateSpecular(GLcontext context, GLmaterial *mat)
{
	int i;
	
	if(!context->enable.Lighting)
	{
		return;
	}
	
	for (i = 0; i <= context->lighting.MaxLight; i++)
	{
		if (context->enable.Light[i])
		{
				COLOR_MULT(context->lighting.Light[i].Scm_by_Scli[mat->Index],
						   mat->Specular,
						   context->lighting.Light[i].Specular);
		}
	}
}

void light_Recalculate(GLcontext context, GLuint light)
{
	/* Unconditionally re-build precalcs... Used when enabling a light */
	COLOR_MULT(context->lighting.Light[light].Acm_by_Acli[FRONT_MAT_INDEX],
			   context->lighting.Material[FRONT_MAT_INDEX].Ambient,
			   context->lighting.Light[light].Ambient);
	COLOR_MULT(context->lighting.Light[light].Acm_by_Acli[BACK_MAT_INDEX],
			   context->lighting.Material[BACK_MAT_INDEX].Ambient,
			   context->lighting.Light[light].Ambient);

	COLOR_MULT(context->lighting.Light[light].Dcm_by_Dcli[FRONT_MAT_INDEX],
			   context->lighting.Material[FRONT_MAT_INDEX].Diffuse,
			   context->lighting.Light[light].Diffuse);
	COLOR_MULT(context->lighting.Light[light].Dcm_by_Dcli[BACK_MAT_INDEX],
			   context->lighting.Material[BACK_MAT_INDEX].Diffuse,
			   context->lighting.Light[light].Diffuse);
			   
	COLOR_MULT(context->lighting.Light[light].Scm_by_Scli[FRONT_MAT_INDEX],
			   context->lighting.Material[FRONT_MAT_INDEX].Specular,
			   context->lighting.Light[light].Specular);
	COLOR_MULT(context->lighting.Light[light].Scm_by_Scli[BACK_MAT_INDEX],
			   context->lighting.Material[BACK_MAT_INDEX].Specular,
			   context->lighting.Light[light].Specular);
			   
	context->lighting.Light[light].OneOverConstantAttenuation
		= 1.0 / context->lighting.Light[light].ConstantAttenuation;
}


void light_CalcDirectionVector(MGLDirection *res, MGLPosition *p1, MGLPosition *p2)
{
	if (p2->w == 0.0 && p1->w != 0.0)
	{
		res->x = p2->x;
		res->y = p2->y;
		res->z = p2->z;
	} 
	else if (p1->w == 0.0 && p2->w != 0.0)
	{
		res->x = -p1->x;
		res->y = -p1->y;
		res->z = -p1->z;
	}
	else if (p1->w == 0.0 && p2->w == 0.0)
	{
		res->x = p2->x - p1->x;
		res->y = p2->y - p1->y;
		res->z = p2->z - p1->z;
	}
	else
	{
#if 0		
		res->x = (p2->x/p2->w) - (p1->x/p1->w);
		res->y = (p2->y/p2->w) - (p1->y/p1->w);
		res->z = (p2->z/p2->w) - (p1->z/p1->w);
#else
		res->x = (p2->x * p1->w) - (p1->x * p2->w);
		res->y = (p2->y * p1->w) - (p1->y * p2->w);
		res->z = (p2->z * p1->w) - (p1->z * p2->w);
#endif		
	}
}

void light_LightVertex(GLcontext context, GLuint vertex, GLfloat area)
{
	MGLColor Cpri;
	GLmaterial *mat;
	int i;
	MGLDirection Normal;
	
	area = -area;

	/* Select the material, depending on two-sided more, and area */
	if (context->lighting.LightModelTwoSide)
	{
		if (context->polygon.FrontFace == GL_CCW)
		{
			if (area <= 0)
			{
				mat = &context->lighting.Material[BACK_MAT_INDEX];
			}
			else
			{
				mat = &context->lighting.Material[FRONT_MAT_INDEX];
			}
		}
		else
		{
			if (-area <= 0)
			{
				mat = &context->lighting.Material[BACK_MAT_INDEX];
			}
			else
			{
				mat = &context->lighting.Material[FRONT_MAT_INDEX];
			}
		}
	}
	else
	{
		mat = &context->lighting.Material[FRONT_MAT_INDEX];
	}

	Normal.x = context->VertexBuffer[vertex].EyeNormal.x;
	Normal.y = context->VertexBuffer[vertex].EyeNormal.y;
	Normal.z = context->VertexBuffer[vertex].EyeNormal.z;

	
	/* Compute Ecm + Acm*Acs */
	COLOR_ADD(Cpri, mat->Emission, mat->Acm_Acs);
	
	/* Compute the per-light terms */
	for (i = 0; i <= context->lighting.MaxLight; i++)
	{
		if (context->enable.Light[i])
		{
			GLfloat atti = 1.0;
			GLfloat spoti = 1.0;
			GLfloat n_dot_VPpli;
			GLfloat VPpli_len;
			GLfloat n_dot_h_hat_i;
			
			MGLDirection VPpli;
			MGLDirection h_hat_i;
			GLlight *light = &context->lighting.Light[i];
			MGLColor Clocal = {0.0, 0.0, 0.0};
	
			light_CalcDirectionVector(&VPpli, &context->VertexBuffer[vertex].eye,
						&light->Position);		
			VPpli_len = VEC_LEN3(VPpli);
			VEC_SCALE3(VPpli, 1.0/VPpli_len);
			
							
			/* Attenuation */
			if (light->Position.w != 0.0)
			{
				if (light->LinearAttenuation == 0.0 && light->QuadraticAttenuation == 0.0)
					atti = light->OneOverConstantAttenuation;
				else
				{
					atti = light->ConstantAttenuation 
						 + (light->LinearAttenuation + light->QuadraticAttenuation * VPpli_len)
						 *  VPpli_len;
					if (atti != 0.0)
						atti = 1.0 / atti;
				}
			}
			
			/* Spotlight effect */
			if (light->SpotCutoff != 180.0f)
			{
				MGLDirection *nSpotDir = &light->NormSpotDirection;
				GLfloat spot_dot_VPpli = -(nSpotDir->x * VPpli.x + nSpotDir->y * VPpli.y + nSpotDir->z * VPpli.z);
				if(context->lighting.LightModelTwoSide && spot_dot_VPpli < 0)
				{
					spot_dot_VPpli = -spot_dot_VPpli;
				}
				if(spot_dot_VPpli >= light->CosineSpotCutoff)
				{
					spoti = pow(spot_dot_VPpli, light->SpotExponent);
				}
				else
				{
					spoti = 0.0;
				}
			}

			/* The rest of the lighting depends on atti and spoti: if one is 0, the
			 * light source contributes nothing, so we ignore it
			 */
			if (atti != 0.0 && spoti != 0.0)
			{
				/* Term 1: Acm * Acli */
				COLOR_ADD(Clocal, Clocal, light->Acm_by_Acli[mat->Index]);

				/* Term 2: (n o VPPli) Dcm * Dcli */
				n_dot_VPpli 
						= max_dot(&Normal, &VPpli);
				
				if (n_dot_VPpli != 0.0)
				{
					COLOR_ADD_SCALED(Clocal, n_dot_VPpli, light->Dcm_by_Dcli[mat->Index]);

					/* Term 3 starts here 
					 * Term 3: (fi)(n o h_hat_i)^Srm * Scm * Scli
					 * Start with h_hat_i
					 */
					if (context->lighting.LightModelLocalViewer)
					{
						MGLDirection VPe;
						
						VPe.x = -context->VertexBuffer[vertex].eye.x;
						VPe.y = -context->VertexBuffer[vertex].eye.y;
						VPe.z = -context->VertexBuffer[vertex].eye.z;
						VEC_NORM3(VPe);
						VEC_ADD3(h_hat_i, VPpli, VPe);
					}
					else
					{
						h_hat_i.x = VPpli.x;
						h_hat_i.y = VPpli.y;
						h_hat_i.z = VPpli.z + 1.0;
					}
					VEC_NORM3(h_hat_i);
					
					n_dot_h_hat_i 
						= max_dot(&Normal, &h_hat_i);

					if (n_dot_h_hat_i != 0)
						COLOR_ADD_SCALED(Clocal, pow(n_dot_h_hat_i, mat->Shininess),
										light->Scm_by_Scli[mat->Index]);
				} /* if (n_dot_VPpli */
			} /* if (atti != 0.0 ... */
			
			/* Finally, add the local contribution to the rest of the bunch */
			COLOR_ADD_SCALED(Cpri, atti*spoti, Clocal);
		} /* if (context->light ... */
	} /* for (i = 0; i <= context->MaxLight ... */
	
	/* Set the vertex color accordingly */
	context->VertexBuffer[vertex].r = CLAMPF(Cpri.r);
	context->VertexBuffer[vertex].g = CLAMPF(Cpri.g);
	context->VertexBuffer[vertex].b = CLAMPF(Cpri.b);
	context->VertexBuffer[vertex].a = CLAMPF(mat->Diffuse.a);
}
	
void cgl_GLMaterialf(struct GLContextIFace *Self, GLenum face, GLenum pname, GLfloat param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Materialf(Self, face, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	switch (pname)
	{
		case GL_SHININESS:
			switch (face)
			{
				case GL_FRONT:
					context->lighting.Material[FRONT_MAT_INDEX].Shininess = param;
					break;
				case GL_BACK:
					context->lighting.Material[BACK_MAT_INDEX].Shininess = param;
					break;
				case GL_FRONT_AND_BACK:
					context->lighting.Material[FRONT_MAT_INDEX].Shininess = param;
					context->lighting.Material[BACK_MAT_INDEX].Shininess = param;
					break;
				default:
					context->CurrentError = GL_INVALID_ENUM;
					return;
			}
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
}


void cgl_GLMaterialfv(struct GLContextIFace *Self, GLenum face, GLenum pname, const GLfloat* param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Materialfv(Self, face, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLmaterial
		*m1 = 0, 
		*m2 = 0;
	
	switch (face)
	{
		case GL_FRONT:
			m1 = &context->lighting.Material[FRONT_MAT_INDEX];
			break;
		case GL_BACK:
			m1 = &context->lighting.Material[BACK_MAT_INDEX];
			break;
		case GL_FRONT_AND_BACK:
			m1 = &context->lighting.Material[FRONT_MAT_INDEX];
			m2 = &context->lighting.Material[BACK_MAT_INDEX];
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
	
	switch (pname)
	{
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
			
		case GL_AMBIENT:
			m1->Ambient.r = param[0];
			m1->Ambient.g = param[1];
			m1->Ambient.b = param[2];
			m1->Ambient.a = param[3];

			COLOR_MULT(m1->Acm_Acs, m1->Ambient, context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, m1);
			
			if (m2)
			{
				m2->Ambient.r = param[0];
				m2->Ambient.g = param[1];
				m2->Ambient.b = param[2];
				m2->Ambient.a = param[3];

				COLOR_MULT(m2->Acm_Acs, m2->Ambient, context->lighting.LightModelAmbient);
				light_UpdateAmbient(context, m2);
			}
			break;
							
		case GL_DIFFUSE:
			m1->Diffuse.r = param[0];
			m1->Diffuse.g = param[1];
			m1->Diffuse.b = param[2];
			m1->Diffuse.a = param[3];

			light_UpdateDiffuse(context, m1);
						
			if (m2)
			{
				m2->Diffuse.r = param[0];
				m2->Diffuse.g = param[1];
				m2->Diffuse.b = param[2];
				m2->Diffuse.a = param[3];
				light_UpdateDiffuse(context, m2);
			}
			break;
			
		case GL_AMBIENT_AND_DIFFUSE:
			m1->Ambient.r = param[0];
			m1->Ambient.g = param[1];
			m1->Ambient.b = param[2];
			m1->Ambient.a = param[3];

			m1->Diffuse.r = param[0];
			m1->Diffuse.g = param[1];
			m1->Diffuse.b = param[2];
			m1->Diffuse.a = param[3];
			
			COLOR_MULT(m1->Acm_Acs, m1->Ambient, context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, m1);
			light_UpdateDiffuse(context, m1);
					
			if (m2)
			{
				m2->Ambient.r = param[0];
				m2->Ambient.g = param[1];
				m2->Ambient.b = param[2];
				m2->Ambient.a = param[3];

				m2->Diffuse.r = param[0];
				m2->Diffuse.g = param[1];
				m2->Diffuse.b = param[2];
				m2->Diffuse.a = param[3];

				COLOR_MULT(m2->Acm_Acs, m2->Ambient, context->lighting.LightModelAmbient);
				light_UpdateAmbient(context, m2);
				light_UpdateDiffuse(context, m2);

			}
			break;
			
		case GL_SPECULAR:
			m1->Specular.r = param[0];
			m1->Specular.g = param[1];
			m1->Specular.b = param[2];
			m1->Specular.a = param[3];
			
			light_UpdateSpecular(context, m1);
					
			if (m2)
			{
				m2->Specular.r = param[0];
				m2->Specular.g = param[1];
				m2->Specular.b = param[2];
				m2->Specular.a = param[3];
				
				light_UpdateSpecular(context, m2);
			}
			break;
			
		case GL_EMISSION:
			m1->Emission.r = param[0];
			m1->Emission.g = param[1];
			m1->Emission.b = param[2];
			m1->Emission.a = param[3];
					
			if (m2)
			{
				m2->Emission.r = param[0];
				m2->Emission.g = param[1];
				m2->Emission.b = param[2];
				m2->Emission.a = param[3];
			}
			break;
			
		case GL_SHININESS:
			m1->Shininess = param[0];
			if (m2)
				m2->Shininess = param[0];
			break;
	}	
}


void cgl_GLMaterialiv(struct GLContextIFace *Self, GLenum face, GLenum pname, const GLint* param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Materialiv(Self, face, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLfloat f[4];
	GLfloat one_over_255 = 1.0/255.0;
			
	switch (pname)
	{
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
			
		case GL_AMBIENT:
		case GL_DIFFUSE:
		case GL_AMBIENT_AND_DIFFUSE:
		case GL_SPECULAR:
		case GL_EMISSION:
			f[0] = (GLfloat)param[0] * one_over_255;
			f[1] = (GLfloat)param[1] * one_over_255;
			f[2] = (GLfloat)param[2] * one_over_255;
			f[3] = (GLfloat)param[3] * one_over_255;
			Self->GLMaterialfv(face, pname, f);
			break;
			
		case GL_SHININESS:
			Self->GLMaterialf(face, pname, (GLfloat)param[0]);
			break;
	}	
}

void cgl_GLLightf(struct GLContextIFace *Self, GLenum light, GLenum pname, GLfloat param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Lightf(Self, light, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLlight *li;
	
	GLFlagError(context, light < GL_LIGHT0 || light > (GL_LIGHT0+MGL_MAX_LIGHTS), 
		GL_INVALID_ENUM);
		
	li = &context->lighting.Light[light - GL_LIGHT0];
	
	switch (pname)
	{
		default:
			context->CurrentError = GL_INVALID_ENUM;
			break;
		case GL_SPOT_EXPONENT:
			GLFlagError(context, param > 128.0, GL_INVALID_VALUE);	
			li->SpotExponent = param;
			break;
		case GL_SPOT_CUTOFF:
		{
			if(param == 180.0)
			{
				li->SpotCutoff = param;
			}
			else
			{
				GLFlagError(context, param > 90.0, GL_INVALID_VALUE);
				li->SpotCutoff = param;
			}
			li->CosineSpotCutoff = cos(li->SpotCutoff * M_PI / 180.0);
			break;
		}
		case GL_CONSTANT_ATTENUATION:
			li->ConstantAttenuation = param;
			li->OneOverConstantAttenuation = 1.0/param;
			break;
		case GL_LINEAR_ATTENUATION:
			li->LinearAttenuation = param;
			break;
		case GL_QUADRATIC_ATTENUATION:
			li->QuadraticAttenuation = param;
			break;
	}
}

void cgl_GLLightfv(struct GLContextIFace *Self, GLenum light_num, GLenum pname, const GLfloat* param)
{
	#define a(x)  (CurrentMV->v[OF_##x])
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Lightfv(Self, light_num, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLlight *light;
	
	GLFlagError(context, light_num < GL_LIGHT0 || light_num > (GL_LIGHT0+MGL_MAX_LIGHTS),
		GL_INVALID_ENUM);
		
	light = &context->lighting.Light[light_num - GL_LIGHT0];
	
	switch (pname)
	{
		default:
			context->CurrentError = GL_INVALID_ENUM;
			break;
			
		case GL_AMBIENT:
			light->Ambient.r = param[0];
			light->Ambient.g = param[1];
			light->Ambient.b = param[2];
			light->Ambient.a = param[3];
			
			COLOR_MULT(light->Acm_by_Acli[FRONT_MAT_INDEX],
					   light->Ambient, 
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient);
			COLOR_MULT(light->Acm_by_Acli[BACK_MAT_INDEX],
					   light->Ambient, 
					   context->lighting.Material[BACK_MAT_INDEX].Ambient);
			break;
			
		case GL_DIFFUSE:
			light->Diffuse.r = param[0];
			light->Diffuse.g = param[1];
			light->Diffuse.b = param[2];
			light->Diffuse.a = param[3];

			COLOR_MULT(light->Dcm_by_Dcli[FRONT_MAT_INDEX],
					   light->Diffuse, 
					   context->lighting.Material[FRONT_MAT_INDEX].Diffuse);
			COLOR_MULT(light->Dcm_by_Dcli[BACK_MAT_INDEX], 
					   light->Diffuse, 
					   context->lighting.Material[FRONT_MAT_INDEX].Diffuse);
			break;
			
		case GL_SPECULAR:
			light->Specular.r = param[0];
			light->Specular.g = param[1];
			light->Specular.b = param[2];
			light->Specular.a = param[3];
			
			COLOR_MULT(light->Scm_by_Scli[FRONT_MAT_INDEX],
					   light->Specular,
					   context->lighting.Material[FRONT_MAT_INDEX].Specular);
			COLOR_MULT(light->Scm_by_Scli[BACK_MAT_INDEX], 
					   light->Specular, 
					   context->lighting.Material[FRONT_MAT_INDEX].Specular);
			break;
			
		case GL_POSITION:
			/* Position is transformed by current modelview matrix */
			light->Position.x = a(11)*param[0] + a(12)*param[1] + a(13)*param[2] + a(14)*param[3];
			light->Position.y = a(21)*param[0] + a(22)*param[1] + a(23)*param[2] + a(24)*param[3];
			light->Position.z = a(31)*param[0] + a(32)*param[1] + a(33)*param[2] + a(34)*param[3];
			light->Position.w = a(41)*param[0] + a(42)*param[1] + a(43)*param[2] + a(44)*param[3];
			break;
			
		case GL_SPOT_DIRECTION:
			/* Direction is only transformed by the upper 3x3 part of modelview */
			light->SpotDirection.x = a(11)*param[0] + a(12)*param[1] + a(13)*param[2];
			light->SpotDirection.y = a(21)*param[0] + a(22)*param[1] + a(23)*param[2];
			light->SpotDirection.z = a(31)*param[0] + a(32)*param[1] + a(33)*param[2];
			light->NormSpotDirection.x = light->SpotDirection.x;
			light->NormSpotDirection.y = light->SpotDirection.y;
			light->NormSpotDirection.z = light->SpotDirection.z;
			VEC_NORM3(light->NormSpotDirection);
			break;			
			
		case GL_SPOT_EXPONENT:
			GLFlagError(context, param[0] > 128.0, GL_INVALID_VALUE);
			light->SpotExponent = param[0];
			break;
			
		case GL_SPOT_CUTOFF:
			GLFlagError(context, param[0] > 90.0, GL_INVALID_VALUE);
			light->SpotCutoff = param[0];
			break;
			
		case GL_CONSTANT_ATTENUATION:
			light->ConstantAttenuation = param[0];
			light->OneOverConstantAttenuation = 1.0 / light->ConstantAttenuation;
			break;
			
		case GL_LINEAR_ATTENUATION:
			light->LinearAttenuation = param[0];
			break;
			
		case GL_QUADRATIC_ATTENUATION:
			light->QuadraticAttenuation = param[0];
			break;
	}
	
	#undef a
}

void cgl_GLLightiv(struct GLContextIFace *Self, GLenum light_num, GLenum pname, const GLint* param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_Lightiv(Self, light_num, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLlight *light;
	GLfloat f[4];
	GLfloat one_over_255 = 1.0 / 255.0;
	
	GLFlagError(context, light_num < GL_LIGHT0 || light_num > (GL_LIGHT0+MGL_MAX_LIGHTS),
		GL_INVALID_ENUM);
		
	light = &context->lighting.Light[light_num - GL_LIGHT0];
	
	switch (pname)
	{
		default:
			context->CurrentError = GL_INVALID_ENUM;
			break;
		case GL_AMBIENT:
			light->Ambient.r = (GLfloat)param[0] * one_over_255;
			light->Ambient.g = (GLfloat)param[1] * one_over_255;
			light->Ambient.b = (GLfloat)param[2] * one_over_255;
			light->Ambient.a = (GLfloat)param[3] * one_over_255;

			COLOR_MULT(light->Acm_by_Acli[FRONT_MAT_INDEX],
					  light->Ambient, 
					  context->lighting.Material[FRONT_MAT_INDEX].Ambient);
			COLOR_MULT(light->Acm_by_Acli[BACK_MAT_INDEX],
					   light->Ambient, 
					   context->lighting.Material[BACK_MAT_INDEX].Ambient);			
			break;
			
		case GL_DIFFUSE:
			light->Diffuse.r = (GLfloat)param[0] * one_over_255;
			light->Diffuse.g = (GLfloat)param[1] * one_over_255;
			light->Diffuse.b = (GLfloat)param[2] * one_over_255;
			light->Diffuse.a = (GLfloat)param[3] * one_over_255;

			COLOR_MULT(light->Dcm_by_Dcli[FRONT_MAT_INDEX],
					   light->Diffuse,
					   context->lighting.Material[FRONT_MAT_INDEX].Diffuse);
			COLOR_MULT(light->Dcm_by_Dcli[BACK_MAT_INDEX],
					   light->Diffuse,
					   context->lighting.Material[FRONT_MAT_INDEX].Diffuse);
			break;
			
		case GL_SPECULAR:
			light->Specular.r = (GLfloat)param[0] * one_over_255;
			light->Specular.g = (GLfloat)param[1] * one_over_255;
			light->Specular.b = (GLfloat)param[2] * one_over_255;
			light->Specular.a = (GLfloat)param[3] * one_over_255;
			
			COLOR_MULT(light->Scm_by_Scli[FRONT_MAT_INDEX],
					   light->Specular,
					   context->lighting.Material[FRONT_MAT_INDEX].Specular);
			COLOR_MULT(light->Scm_by_Scli[BACK_MAT_INDEX],
					   light->Specular,
					   context->lighting.Material[FRONT_MAT_INDEX].Specular);
			break;
			
		case GL_POSITION:
			f[0] = (GLfloat)param[0];
			f[1] = (GLfloat)param[1];
			f[2] = (GLfloat)param[2];
			f[3] = (GLfloat)param[3];
			Self->GLLightfv(light_num, pname, f);
			break;
			
		case GL_SPOT_DIRECTION:
			f[0] = (GLfloat)param[0];
			f[1] = (GLfloat)param[1];
			f[2] = (GLfloat)param[2];
			Self->GLLightfv(light_num, pname, f);
			break;
			
		case GL_SPOT_EXPONENT:
			GLFlagError(context, param[0] > 128, GL_INVALID_VALUE);
			light->SpotExponent = (GLfloat)param[0];
			break;
			
		case GL_SPOT_CUTOFF:
			GLFlagError(context, param[0] > 90, GL_INVALID_VALUE);
			light->SpotCutoff = (GLfloat)param[0];
			break;
			
		case GL_CONSTANT_ATTENUATION:
			light->ConstantAttenuation = (GLfloat)param[0];
			light->OneOverConstantAttenuation = 1.0 / light->ConstantAttenuation;
			break;
			
		case GL_LINEAR_ATTENUATION:
			light->LinearAttenuation = (GLfloat)param[0];
			break;
			
		case GL_QUADRATIC_ATTENUATION:
			light->QuadraticAttenuation = (GLfloat)param[0];
			break;
	}
}

void cgl_GLLightModelf(struct GLContextIFace *Self, GLenum pname, GLfloat param)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_LightModelf(Self, pname, param);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	switch (pname)
	{
		case GL_LIGHT_MODEL_LOCAL_VIEWER:
			context->lighting.LightModelLocalViewer = (param != 0);
			break;
		case GL_LIGHT_MODEL_TWO_SIDE:
			context->lighting.LightModelTwoSide = (param != 0);
			break;
		case GL_LIGHT_MODEL_COLOR_CONTROL:
			context->lighting.LightModelColorControl = param;
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
	}
}

void cgl_GLLightModelfv(struct GLContextIFace *Self, GLenum pname, const GLfloat* params)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_LightModelfv(Self, pname, params);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	switch (pname)
	{
		case GL_LIGHT_MODEL_AMBIENT:
			context->lighting.LightModelAmbient.r = params[0];
			context->lighting.LightModelAmbient.g = params[1];
			context->lighting.LightModelAmbient.b = params[2];
			context->lighting.LightModelAmbient.a = params[3];
			
			COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
			COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[BACK_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
			break;
			
		case GL_LIGHT_MODEL_LOCAL_VIEWER:
			context->lighting.LightModelLocalViewer = (params[0] != 0.0);
			break;
			
		case GL_LIGHT_MODEL_TWO_SIDE:
			context->lighting.LightModelTwoSide = (params[0] != 0.0);
			break;

		case GL_LIGHT_MODEL_COLOR_CONTROL:
			context->lighting.LightModelColorControl = params[0];
			break;

		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
}


void cgl_GLLightModeliv(struct GLContextIFace *Self, GLenum pname, const GLint* params)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_LightModeliv(Self, pname, params);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	GLfloat one_over_255 = 1.0 / 255.0;
	
	switch (pname)
	{
		case GL_LIGHT_MODEL_AMBIENT:
			context->lighting.LightModelAmbient.r = (GLfloat)params[0] * one_over_255;
			context->lighting.LightModelAmbient.g = (GLfloat)params[1] * one_over_255;
			context->lighting.LightModelAmbient.b = (GLfloat)params[2] * one_over_255;
			context->lighting.LightModelAmbient.a = (GLfloat)params[3] * one_over_255;

			COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
			COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[BACK_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
			break;
			
		case GL_LIGHT_MODEL_LOCAL_VIEWER:
			context->lighting.LightModelLocalViewer = (params[0] != 0);
			break;

		case GL_LIGHT_MODEL_TWO_SIDE:
			context->lighting.LightModelTwoSide = (params[0] != 0);
			break;

		case GL_LIGHT_MODEL_COLOR_CONTROL:
			context->lighting.LightModelColorControl = params[0];
			break;

		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
}

void cgl_GLColorMaterial(struct GLContextIFace *Self, GLenum face, GLenum mode)
{
	GLcontext context = GET_INSTANCE(Self);

    if(dl_IsDLActive(Self)){
		dl_save_ColorMaterial(Self, face, mode);
		if(!dl_CompileAndExecuteMode(Self)){
			return;
		}
	}

	switch (mode)
	{
		case GL_EMISSION:
		case GL_AMBIENT:
		case GL_DIFFUSE:
		case GL_SPECULAR:
		case GL_AMBIENT_AND_DIFFUSE:
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
	
	context->lighting.ColorMaterialParameter = mode;
	context->lighting.ColorMaterialFace = face;
			
	switch (face)
	{
		case GL_FRONT:
			switch (mode)	
			{	
				case GL_EMISSION:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0] 
						= &context->lighting.Material[FRONT_MAT_INDEX].Emission;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1] 
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update 
						= 0;
					break;
				case GL_AMBIENT:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0] 
						= &context->lighting.Material[FRONT_MAT_INDEX].Ambient;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1] 
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update 
						= MAT_UPDATE_AMBIENT;
					break;
				case GL_DIFFUSE:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Diffuse;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1] 
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update 
						= MAT_UPDATE_DIFFUSE;
					break;
				case GL_SPECULAR:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Specular;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update 
						= MAT_UPDATE_SPECULAR;
					break;		
				case GL_AMBIENT_AND_DIFFUSE:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Ambient;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= &context->lighting.Material[FRONT_MAT_INDEX].Diffuse;
					context->lighting.Material[FRONT_MAT_INDEX].Update 
						= MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
					break;
				default:
					context->CurrentError = GL_INVALID_ENUM;
					return;
			}
			break;
		case GL_BACK:
			switch (mode)	
			{	
				case GL_EMISSION:
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Emission;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= 0;
					break;
				case GL_AMBIENT:
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Ambient;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT;
					break;
				case GL_DIFFUSE:
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Diffuse;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_DIFFUSE;
					break;
				case GL_SPECULAR:
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Specular;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_SPECULAR;
					break;		
				case GL_AMBIENT_AND_DIFFUSE:
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Ambient;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= &context->lighting.Material[BACK_MAT_INDEX].Diffuse;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
					break;
				default:
					context->CurrentError = GL_INVALID_ENUM;
					return;
			}
			break;
		case GL_FRONT_AND_BACK:
			switch (mode)	
			{	
				case GL_EMISSION:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Emission;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Emission;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update 
						= 0;
					break;
				case GL_AMBIENT:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Ambient;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Ambient;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT;
					break;
				case GL_DIFFUSE:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Diffuse;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update
						= MAT_UPDATE_DIFFUSE;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Diffuse;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_DIFFUSE;
					break;
				case GL_SPECULAR:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Specular;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[FRONT_MAT_INDEX].Update
						= MAT_UPDATE_SPECULAR;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Specular;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= 0;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_SPECULAR;
					break;		
				case GL_AMBIENT_AND_DIFFUSE:
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[FRONT_MAT_INDEX].Ambient;
					context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]
						= &context->lighting.Material[FRONT_MAT_INDEX].Diffuse;
					context->lighting.Material[FRONT_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]
						= &context->lighting.Material[BACK_MAT_INDEX].Ambient;
					context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]
						= &context->lighting.Material[BACK_MAT_INDEX].Diffuse;
					context->lighting.Material[BACK_MAT_INDEX].Update
						= MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
					break;
				default:
					context->CurrentError = GL_INVALID_ENUM;
					return;
			}
			break;
	}
}
		

void light_UpdateColorMaterial(GLcontext context)
{
	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->r
						= context->current.CurrentColor.r;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->g
						= context->current.CurrentColor.g;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->b
						= context->current.CurrentColor.b;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->a
						= context->current.CurrentColor.a;
	}

	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->r
						= context->current.CurrentColor.r;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->g
						= context->current.CurrentColor.g;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->b
						= context->current.CurrentColor.b;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->a
						= context->current.CurrentColor.a;
	}
		
	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->r
						= context->current.CurrentColor.r;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->g
						= context->current.CurrentColor.g;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->b
						= context->current.CurrentColor.b;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->a
						= context->current.CurrentColor.a;
	}


	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->r
						= context->current.CurrentColor.r;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->g
						= context->current.CurrentColor.g;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->b
						= context->current.CurrentColor.b;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->a
						= context->current.CurrentColor.a;
	}


	if (context->lighting.Material[FRONT_MAT_INDEX].Update)
	{
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[FRONT_MAT_INDEX]);
		}
	
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[FRONT_MAT_INDEX]);

		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[FRONT_MAT_INDEX]);
	}
	
	if (context->lighting.Material[BACK_MAT_INDEX].Update)
	{
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[BACK_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[BACK_MAT_INDEX]);
		}
	
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[BACK_MAT_INDEX]);

		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[BACK_MAT_INDEX]);
	}
}


void light_UpdateColorMaterialColor(GLcontext context, const GLfloat color[4])
{
	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->r
						= color[0];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->g
						= color[1];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->b
						= color[2];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->a
						= color[3];
	}

	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->r
						= color[0];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->g
						= color[1];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->b
						= color[2];
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->a
						= color[3];
	}
		
	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->r
						= color[0];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->g
						= color[1];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->b
						= color[2];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->a
						= color[3];
	}


	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->r
						= color[0];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->g
						= color[1];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->b
						= color[2];
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->a
						= color[3];
	}


	if (context->lighting.Material[FRONT_MAT_INDEX].Update)
	{
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[FRONT_MAT_INDEX]);
		}
	
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[FRONT_MAT_INDEX]);

		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[FRONT_MAT_INDEX]);
	}
	
	if (context->lighting.Material[BACK_MAT_INDEX].Update)
	{
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[BACK_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[BACK_MAT_INDEX]);
		}
	
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[BACK_MAT_INDEX]);

		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[BACK_MAT_INDEX]);
	}
}

void light_UpdateColorMaterialVertex(GLcontext context)
{
	MGLVertex *current = &context->VertexBuffer[context->VertexBufferPointer];	
	
	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->r 
						= current->r;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->g
						= current->g;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->b 
						= current->b;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0]->a
						= current->a;
	}

	if (context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->r 
						= current->r;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->g
						= current->g;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->b 
						= current->b;
		context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1]->a
						= current->a;
	}

		
	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->r 
						= current->r;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->g
						= current->g;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->b 
						= current->b;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0]->a
						= current->a;
	}


	if (context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1])
	{
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->r 
						= current->r;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->g
						= current->g;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->b 
						= current->b;
		context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1]->a
						= current->a;
	}
	

	if (context->lighting.Material[FRONT_MAT_INDEX].Update)
	{
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[FRONT_MAT_INDEX]);
		}
	
		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[FRONT_MAT_INDEX]);

		if (context->lighting.Material[FRONT_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[FRONT_MAT_INDEX]);
	}
	
	if (context->lighting.Material[BACK_MAT_INDEX].Update)
	{
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_AMBIENT)
		{
			COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs,
					   context->lighting.Material[BACK_MAT_INDEX].Ambient,
					   context->lighting.LightModelAmbient);
			light_UpdateAmbient(context, &context->lighting.Material[BACK_MAT_INDEX]);
		}
	
		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_DIFFUSE)
			light_UpdateDiffuse(context, &context->lighting.Material[BACK_MAT_INDEX]);

		if (context->lighting.Material[BACK_MAT_INDEX].Update & MAT_UPDATE_SPECULAR)
			light_UpdateSpecular(context, &context->lighting.Material[BACK_MAT_INDEX]);
	}
}

void cgl_GLGetLightfv(struct GLContextIFace *Self, GLenum light_num, GLenum pname, GLfloat *param)
{
	GLcontext context = GET_INSTANCE(Self);

    dprintf("called\n");

	GLlight *light;
	
	GLFlagError(context, light_num < GL_LIGHT0 || light_num > (GL_LIGHT0+MGL_MAX_LIGHTS),
		GL_INVALID_ENUM);
		
	light = &context->lighting.Light[light_num - GL_LIGHT0];
	
	switch (pname)
	{	
		case GL_AMBIENT:
			param[0] = light->Ambient.r;
			param[1] = light->Ambient.g;
			param[2] = light->Ambient.b;
			param[3] = light->Ambient.a;
			break;
			
		case GL_DIFFUSE:
			param[0] = light->Diffuse.r;
			param[1] = light->Diffuse.g;
			param[2] = light->Diffuse.b;
			param[3] = light->Diffuse.a;
			break;
			
		case GL_SPECULAR:
			param[0] = light->Specular.r;
			param[1] = light->Specular.g;
			param[2] = light->Specular.b;
			param[3] = light->Specular.a;
			break;
			
		case GL_POSITION:
			/* To be considered: is this correct ? The specs say that these are
			 * returned in eye coordinates, which means they are transformed
			 * by the current modelview already.
			 */ 
			param[0] = light->Position.x;
			param[1] = light->Position.y;
			param[2] = light->Position.z;
			param[3] = light->Position.w;
			break;
			
		case GL_SPOT_DIRECTION:
			/* To be considered: is this correct ? The specs say that these are
			 * returned in eye coordinates, which means they are transformed
			 * by the current modelview already.
			 */ 
			param[0] = light->SpotDirection.x;
			param[1] = light->SpotDirection.y;
			param[2] = light->SpotDirection.z;
			break;			
			
		case GL_SPOT_EXPONENT:
			param[0] = light->SpotExponent;
			break;
			
		case GL_SPOT_CUTOFF:
			param[0] = light->SpotCutoff ;
			break;
			
		case GL_CONSTANT_ATTENUATION:
			param[0] = light->ConstantAttenuation;
			break;
			
		case GL_LINEAR_ATTENUATION:
			param[0] = light->LinearAttenuation;
			break;
			
		case GL_QUADRATIC_ATTENUATION:
			param[0] = light->QuadraticAttenuation;
			break;		
	
		default:
			context->CurrentError = GL_INVALID_ENUM;
			break;
	}
}

void cgl_GLGetLightiv(struct GLContextIFace *Self, GLenum light_num, GLenum pname, GLint *param)
{
	GLcontext context = GET_INSTANCE(Self);

    dprintf("called\n");

	GLlight *light;
	
	GLFlagError(context, light_num < GL_LIGHT0 || light_num > (GL_LIGHT0+MGL_MAX_LIGHTS),
		GL_INVALID_ENUM);
		
	light = &context->lighting.Light[light_num - GL_LIGHT0];
	
	switch (pname)
	{	
		case GL_AMBIENT:
			param[0] = (GLint)light->Ambient.r;
			param[1] = (GLint)light->Ambient.g;
			param[2] = (GLint)light->Ambient.b;
			param[3] = (GLint)light->Ambient.a;
			break;
			
		case GL_DIFFUSE:
			param[0] = (GLint)light->Diffuse.r;
			param[1] = (GLint)light->Diffuse.g;
			param[2] = (GLint)light->Diffuse.b;
			param[3] = (GLint)light->Diffuse.a;
			break;
			
		case GL_SPECULAR:
			param[0] = (GLint)light->Specular.r;
			param[1] = (GLint)light->Specular.g;
			param[2] = (GLint)light->Specular.b;
			param[3] = (GLint)light->Specular.a;
			break;
			
		case GL_POSITION:
			/* To be considered: is this correct ? The specs say that these are
			 * returned in eye coordinates, which means they are transformed
			 * by the current modelview already.
			 */ 
			param[0] = (GLint)light->Position.x;
			param[1] = (GLint)light->Position.y;
			param[2] = (GLint)light->Position.z;
			param[3] = (GLint)light->Position.w;
			break;
			
		case GL_SPOT_DIRECTION:
			/* To be considered: is this correct ? The specs say that these are
			 * returned in eye coordinates, which means they are transformed
			 * by the current modelview already.
			 */ 
			param[0] = (GLint)light->SpotDirection.x;
			param[1] = (GLint)light->SpotDirection.y;
			param[2] = (GLint)light->SpotDirection.z;
			break;			
			
		case GL_SPOT_EXPONENT:
			param[0] = (GLint)light->SpotExponent;
			break;
			
		case GL_SPOT_CUTOFF:
			param[0] = (GLint)light->SpotCutoff ;
			break;
			
		case GL_CONSTANT_ATTENUATION:
			param[0] = (GLint)light->ConstantAttenuation;
			break;
			
		case GL_LINEAR_ATTENUATION:
			param[0] = (GLint)light->LinearAttenuation;
			break;
			
		case GL_QUADRATIC_ATTENUATION:
			param[0] = (GLint)light->QuadraticAttenuation;
			break;		
	
		default:
			context->CurrentError = GL_INVALID_ENUM;
			break;
	}
}
#endif


void light_Init(GLcontext context)
{
#ifdef MGL_USE_LIGHTING
	int i;
	
	kprintf("Initializing lighting\n");
	
	context->enable.Lighting = GL_FALSE;
	context->enable.ColorMaterial = GL_FALSE;
	
	MGL_SET_COLOR(context->lighting.LightModelAmbient, 0.2, 0.2, 0.2, 1.0);
	context->lighting.LightModelLocalViewer = GL_FALSE;
	context->lighting.LightModelTwoSide = GL_FALSE;
	context->lighting.LightModelColorControl = GL_SINGLE_COLOR;
	
	context->lighting.Material[FRONT_MAT_INDEX].Index = FRONT_MAT_INDEX;
	MGL_SET_COLOR(context->lighting.Material[FRONT_MAT_INDEX].Ambient, 0.2, 0.2, 0.2, 1.0);
	MGL_SET_COLOR(context->lighting.Material[FRONT_MAT_INDEX].Diffuse, 0.8, 0.8, 0.8, 1.0);
	MGL_SET_COLOR(context->lighting.Material[FRONT_MAT_INDEX].Specular, 0.0, 0.0, 0.0, 1.0);
	MGL_SET_COLOR(context->lighting.Material[FRONT_MAT_INDEX].Emission, 0.0, 0.0, 0.0, 1.0);
	context->lighting.Material[FRONT_MAT_INDEX].Shininess = 0.0;

	context->lighting.Material[BACK_MAT_INDEX].Index = BACK_MAT_INDEX;
	MGL_SET_COLOR(context->lighting.Material[BACK_MAT_INDEX].Ambient, 0.2, 0.2, 0.2, 1.0);
	MGL_SET_COLOR(context->lighting.Material[BACK_MAT_INDEX].Diffuse, 0.8, 0.8, 0.8, 1.0);
	MGL_SET_COLOR(context->lighting.Material[BACK_MAT_INDEX].Specular, 0.0, 0.0, 0.0, 1.0);
	MGL_SET_COLOR(context->lighting.Material[BACK_MAT_INDEX].Emission, 0.0, 0.0, 0.0, 1.0);
	context->lighting.Material[BACK_MAT_INDEX].Shininess = 0.0;	
	
	context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[0] = &context->lighting.Material[FRONT_MAT_INDEX].Ambient;
	context->lighting.Material[FRONT_MAT_INDEX].ColorMaterial[1] = &context->lighting.Material[FRONT_MAT_INDEX].Diffuse;
	context->lighting.Material[FRONT_MAT_INDEX].Update = MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
	context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[0] = &context->lighting.Material[BACK_MAT_INDEX].Ambient;
	context->lighting.Material[BACK_MAT_INDEX].ColorMaterial[1] = &context->lighting.Material[BACK_MAT_INDEX].Diffuse;
	context->lighting.Material[BACK_MAT_INDEX].Update = MAT_UPDATE_AMBIENT|MAT_UPDATE_DIFFUSE;
		
	for (i = 0; i < MGL_MAX_LIGHTS; i++)
	{
		context->enable.Light[i] = GL_FALSE;
		
		MGL_SET_COLOR(context->lighting.Light[i].Ambient, 0.0, 0.0, 0.0, 1.0);
		
		if (i == 0)
		{
			MGL_SET_COLOR(context->lighting.Light[i].Diffuse, 1.0, 1.0, 1.0, 1.0);
			MGL_SET_COLOR(context->lighting.Light[i].Specular, 1.0, 1.0, 1.0, 1.0);
		}
		else
		{
			MGL_SET_COLOR(context->lighting.Light[i].Diffuse, 0.0, 0.0, 0.0, 1.0);
			MGL_SET_COLOR(context->lighting.Light[i].Specular, 0.0, 0.0, 0.0, 1.0);
		}
		
		context->lighting.Light[i].Position.x = 0.0;
		context->lighting.Light[i].Position.y = 0.0;
		context->lighting.Light[i].Position.z = 1.0;
		context->lighting.Light[i].Position.w = 0.0;
		
		context->lighting.Light[i].SpotDirection.x = 0.0;
		context->lighting.Light[i].SpotDirection.y = 0.0;
		context->lighting.Light[i].SpotDirection.z = -1.0;
		context->lighting.Light[i].NormSpotDirection.x = 0.0;
		context->lighting.Light[i].NormSpotDirection.y = 0.0;
		context->lighting.Light[i].NormSpotDirection.z = -1.0;
		
		context->lighting.Light[i].SpotExponent = 0.0;
		context->lighting.Light[i].SpotCutoff = 180.0f;
		context->lighting.Light[i].ConstantAttenuation = 1.0;
		context->lighting.Light[i].LinearAttenuation = 0.0;
		context->lighting.Light[i].QuadraticAttenuation = 0.0;
		context->lighting.Light[i].OneOverConstantAttenuation = 1.0;
		context->lighting.Light[i].CosineSpotCutoff = -1.0;	
	}
	
	COLOR_MULT(context->lighting.Material[FRONT_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[FRONT_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
	COLOR_MULT(context->lighting.Material[BACK_MAT_INDEX].Acm_Acs, 
					   context->lighting.Material[BACK_MAT_INDEX].Ambient, 
					   context->lighting.LightModelAmbient);
	context->lighting.MaxLight = -1;
#endif
}


void cgl_GLGetMaterialfv(struct GLContextIFace *Self, GLenum face, GLenum value, GLfloat *data)
{
	GLcontext context = GET_INSTANCE(Self);
	
	GLmaterial *m1;
	
	switch (face)
	{
		case GL_FRONT:
			m1 = &context->lighting.Material[FRONT_MAT_INDEX];
			break;
		case GL_BACK:
			m1 = &context->lighting.Material[BACK_MAT_INDEX];
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}

	switch (value)
	{
		case GL_AMBIENT:
			*data ++ = m1->Ambient.r;
			*data ++ = m1->Ambient.g;
			*data ++ = m1->Ambient.b;
			*data ++ = m1->Ambient.a;
			break;
		case GL_DIFFUSE:
			*data ++ = m1->Diffuse.r;
			*data ++ = m1->Diffuse.g;
			*data ++ = m1->Diffuse.b;
			*data ++ = m1->Diffuse.a;
			break;
		case GL_SPECULAR:
			*data ++ = m1->Specular.r;
			*data ++ = m1->Specular.g;
			*data ++ = m1->Specular.b;
			*data ++ = m1->Specular.a;
			break;
		case GL_EMISSION:
			*data ++ = m1->Emission.r;
			*data ++ = m1->Emission.g;
			*data ++ = m1->Emission.b;
			*data ++ = m1->Emission.a;
			break;
		case GL_SHININESS:
			*data ++ = m1->Shininess;
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
}


void cgl_GLGetMaterialiv(struct GLContextIFace *Self, GLenum face, GLenum value, GLint *data)
{
	GLcontext context = GET_INSTANCE(Self);
	
	GLmaterial *m1;
	
	switch (face)
	{
		case GL_FRONT:
			m1 = &context->lighting.Material[FRONT_MAT_INDEX];
			break;
		case GL_BACK:
			m1 = &context->lighting.Material[BACK_MAT_INDEX];
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}

	switch (value)
	{
		case GL_AMBIENT:
			*data ++ = (GLint)m1->Ambient.r;
			*data ++ = (GLint)m1->Ambient.g;
			*data ++ = (GLint)m1->Ambient.b;
			*data ++ = (GLint)m1->Ambient.a;
			break;
		case GL_DIFFUSE:
			*data ++ = (GLint)m1->Diffuse.r;
			*data ++ = (GLint)m1->Diffuse.g;
			*data ++ = (GLint)m1->Diffuse.b;
			*data ++ = (GLint)m1->Diffuse.a;
			break;
		case GL_SPECULAR:
			*data ++ = (GLint)m1->Specular.r;
			*data ++ = (GLint)m1->Specular.g;
			*data ++ = (GLint)m1->Specular.b;
			*data ++ = (GLint)m1->Specular.a;
			break;
		case GL_EMISSION:
			*data ++ = (GLint)m1->Emission.r;
			*data ++ = (GLint)m1->Emission.g;
			*data ++ = (GLint)m1->Emission.b;
			*data ++ = (GLint)m1->Emission.a;
			break;
		case GL_SHININESS:
			*data ++ = (GLint)m1->Shininess;
			break;
		default:
			context->CurrentError = GL_INVALID_ENUM;
			return;
	}
}
