/*
 * $Id$
 *
 * $Date$
 * $Revision$
 *
 * (C) 1999 by Hyperion
 * All rights reserved
 *
 * This file is part of the MiniGL library project
 * See the file Licence.txt for more details
 *
 */

#include "sysinc.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <proto/graphics.h>

static char rcsid[] UNUSED = "$Id$";

void cgl_GLLineWidth(struct GLContextIFace *Self, GLfloat lineWidth)
{
	GLcontext context = GET_INSTANCE(Self);
	
	context->line.LineWidth = lineWidth;
#ifdef MGL_NEW_WARP3D
	IWarp3D->W3D_SetParameter(context->w3dContext, W3D_LINE_WIDTH, &context->line.LineWidth);
#endif
}

void cgl_GLPointSize(struct GLContextIFace *Self, GLfloat size)
{
	GLcontext context = GET_INSTANCE(Self);

	context->point.PointSize = size;
#ifdef MGL_NEW_WARP3D
	IWarp3D->W3D_SetParameter(context->w3dContext, W3D_POINT_SIZE, &context->point.PointSize);
#endif
}

void cgl_GLLineStipple(struct GLContextIFace *Self, GLint factor, GLushort pattern)
{
#ifdef MGL_NEW_WARP3D
	GLcontext context = GET_INSTANCE(Self);

	/* FIXME: Add states to context */
	IWarp3D->W3D_SetParameter(context->w3dContext, W3D_STIPPLE_LINE, &pattern);
	IWarp3D->W3D_SetParameter(context->w3dContext, W3D_STIPPLE_LINE_FACTOR, &factor);
#endif
}

void cgl_GLPolygonStipple(struct GLContextIFace *Self, GLubyte *pattern)
{
#ifdef MGL_NEW_WARP3D
	GLcontext context = GET_INSTANCE(Self);
	
	memcpy(context->polygon_stipple.pattern, pattern, 128);
	
	IWarp3D->W3D_SetParameter(context->w3dContext, W3D_STIPPLE_POLYGON, &pattern);
#endif
}

void cgl_GLPolygonOffset(struct GLContextIFace *Self, GLfloat factor, GLfloat units)
{
	GLcontext context = GET_INSTANCE(Self);

	context->polygon.PolygonOffsetFactor = factor;
	context->polygon.PolygonOffsetUnits = units;
}


void cgl_GLAlphaFunc(struct GLContextIFace *Self, GLenum func, GLclampf ref)
{
	GLcontext context = GET_INSTANCE(Self);

	ULONG w3dmode;
	W3D_Float refvalue = (W3D_Float)ref;

	GLFlagError(context, context->CurrentPrimitive != GL_BASE, GL_INVALID_OPERATION);

	switch(func)
	{
		case GL_NEVER:
			w3dmode = W3D_A_NEVER;
			break;
		case GL_LESS:
			w3dmode = W3D_A_LESS;
			break;
		case GL_EQUAL:
			w3dmode = W3D_A_EQUAL;
			break;
		case GL_LEQUAL:
			w3dmode = W3D_A_LEQUAL;
			break;
		case GL_GREATER:
			w3dmode = W3D_A_GREATER;
			break;
		case GL_NOTEQUAL:
			w3dmode = W3D_A_NOTEQUAL;
			break;
		case GL_GEQUAL:
			w3dmode = W3D_A_GEQUAL;
			break;
		case GL_ALWAYS:
			w3dmode = W3D_A_ALWAYS;
			break;
		default:
			GLFlagError(context, 1, GL_INVALID_ENUM);
			break;
	}

	IWarp3D->W3D_SetAlphaMode(context->w3dContext, w3dmode, &refvalue);

}

void cgl_GLDrawBuffer(struct GLContextIFace *Self, GLenum mode)
{
}

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

	switch(face)
	{
	case GL_FRONT:
		context->polygon.PolygonModeFront = mode;
		break;
	case GL_BACK:
		context->polygon.PolygonModeBack = mode;
		break;
	case GL_FRONT_AND_BACK:
		context->polygon.PolygonModeFront = mode;
		context->polygon.PolygonModeBack = mode;
		break;
	}
   	return ;
}

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

	context->lighting.ShadeModel = mode ;

	if (mode == GL_FLAT)
		IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD, W3D_DISABLE);
	else if (mode == GL_SMOOTH)
		IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD, W3D_ENABLE);
}



#define BLS(X) case GL_##X: src=W3D_##X; break
#define BLD(X) case GL_##X: dest=W3D_##X; break

void cgl_GLBlendFunc(struct GLContextIFace *Self, GLenum sfactor, GLenum dfactor)
{
	GLcontext context = GET_INSTANCE(Self);

	ULONG src = 0, dest = 0;

	context->color_buffer.BlendSrc = sfactor ;
	context->color_buffer.BlendDst = dfactor ;

	switch(sfactor)
	{
		BLS(ZERO);
		BLS(ONE);
		BLS(DST_COLOR);
		BLS(ONE_MINUS_DST_COLOR);
		BLS(SRC_ALPHA);
		BLS(ONE_MINUS_SRC_ALPHA);
		BLS(DST_ALPHA);
		BLS(ONE_MINUS_DST_ALPHA);
		BLS(SRC_ALPHA_SATURATE);
	default:
		GLFlagError(context, 1, GL_INVALID_ENUM);
	}

	switch(dfactor)
	{
		BLD(ZERO);
		BLD(ONE);
		BLD(SRC_COLOR);
		BLD(ONE_MINUS_SRC_COLOR);
		BLD(SRC_ALPHA);
		BLD(ONE_MINUS_SRC_ALPHA);
		BLD(DST_ALPHA);
		BLD(ONE_MINUS_DST_ALPHA);
	}
	// Try to set the mode, if unavailable, switch to
	// (SRC_ALPHA, ONE_MINUS_SRC_ALPHA) which is supported
	// by almost all Amiga supported graphics cards
	if (IWarp3D->W3D_SetBlendMode(context->w3dContext, src, dest) 
												== W3D_UNSUPPORTEDBLEND)
	{
		if (context->NoFallbackAlpha == GL_FALSE)
		{
			IWarp3D->W3D_SetBlendMode(context->w3dContext, 
									W3D_SRC_ALPHA, W3D_ONE_MINUS_SRC_ALPHA);
			context->AlphaFellBack = GL_TRUE;
		}
		else
		{
			context->AlphaFellBack = GL_FALSE;
		}
	}
	else
	{
		context->AlphaFellBack = GL_FALSE;
	}
}

void cgl_GLStencilFunc(struct GLContextIFace *Self, GLenum func, GLint ref, GLint mask)
{
	GLcontext context = GET_INSTANCE(Self);

	uint32 f = W3D_ST_NEVER;
	switch (func)
	{
	case GL_NEVER: 		f = W3D_ST_NEVER; break;
	case GL_ALWAYS: 	f = W3D_ST_ALWAYS; break;
	case GL_LESS:		f = W3D_ST_LESS; break;
	case GL_LEQUAL:		f = W3D_ST_LEQUAL; break;
	case GL_EQUAL:		f = W3D_ST_EQUAL; break;
	case GL_GEQUAL:		f = W3D_ST_GEQUAL; break;
	case GL_GREATER:	f = W3D_ST_GREATER; break;
	case GL_NOTEQUAL:	f = W3D_ST_NOTEQUAL; break;
	}
	
	IWarp3D->W3D_SetStencilFunc(context->w3dContext, f, ref, mask);
}

static uint32 MapOp(GLenum op)
{
	switch(op)
	{
	case GL_KEEP: 		return W3D_ST_KEEP;
	case GL_ZERO: 		return W3D_ST_ZERO;
	case GL_REPLACE:	return W3D_ST_REPLACE;
	case GL_INCR:		return W3D_ST_INCR;
	case GL_DECR:		return W3D_ST_DECR;
	case GL_INVERT:		return W3D_ST_INVERT;
	default:			return 0;
	}
}

void cgl_GLStencilOp(struct GLContextIFace *Self, GLenum sfail, GLenum dpfail, GLenum dppass)
{
	GLcontext context = GET_INSTANCE(Self);

	uint32
		sf = MapOp(sfail),
		dpf = MapOp(dpfail),
		dpp = MapOp(dppass);
	
	IWarp3D->W3D_SetStencilOp(context->w3dContext, sf, dpf, dpp);
}

void cgl_GLStencilMask(struct GLContextIFace *Self, GLuint mask)
{
	GLcontext context = GET_INSTANCE(Self);

	IWarp3D->W3D_SetWriteMask(context->w3dContext, mask);
}

void cgl_GLColorMask(struct GLContextIFace *Self, GLboolean red, GLboolean green, 
	GLboolean blue, GLboolean alpha)
{
	GLcontext context = GET_INSTANCE(Self);
	
	context->color_buffer.WriteMaskRed = red;
	context->color_buffer.WriteMaskGreen = green;
	context->color_buffer.WriteMaskBlue = blue;
	context->color_buffer.WriteMaskAlpha = alpha;
	
	IWarp3D->W3D_SetColorMask(context->w3dContext, red, green, blue, alpha);
}

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

	ULONG hint = W3D_H_AVERAGE;
	
	switch(mode)
	{
		case GL_FASTEST:    
			hint = W3D_H_FAST; 
			break;
		case GL_NICEST:     
			hint = W3D_H_NICE; 
			break;
		case GL_DONT_CARE:  
			hint = W3D_H_AVERAGE; 
			break;
		default:
//			GLFlagError(context, 1, GL_INVALID_ENUM);
			break;
	}

	switch(target)
	{
		case GL_FOG_HINT:
			IWarp3D->W3D_Hint(context->w3dContext, W3D_H_FOGGING, hint);
			break;
		case GL_PERSPECTIVE_CORRECTION_HINT:
			IWarp3D->W3D_Hint(context->w3dContext, W3D_H_PERSPECTIVE, hint);
			break;
		case MGL_W_ONE_HINT:
			if (mode == GL_FASTEST) 
				context->WOne_Hint = GL_TRUE;
			else
				context->WOne_Hint = GL_FALSE;
			break;
		case MGL_TEXTURE_QUALITY_HINT:
			if (mode == GL_FASTEST)
				context->ConvertTo16Bit = GL_TRUE;
			else
				context->ConvertTo16Bit = GL_FALSE;
			break;
		default:
//			GLFlagError(context, 1, GL_INVALID_ENUM);
			break;
	}
}



static void BuildExtensionString(GLcontext context)
{
#define DEFAULT_EXT "GL_MGL_packed_pixels GL_EXT_packed_pixels GL_EXT_bgra GL_EXT_color_table GL_EXT_vertex_array GL_NV_texgen_reflection"

	uint32 size;
	uint32 env_combine = IWarp3D->W3D_Query(context->w3dContext, W3D_Q_ENV_COMBINE, 0);
	uint32 env_crossbar = IWarp3D->W3D_Query(context->w3dContext, W3D_Q_ENV_CROSSBAR, 0);
	uint32 env_add = IWarp3D->W3D_Query(context->w3dContext, W3D_Q_ENV_ADD, 0);

	size = strlen(DEFAULT_EXT) + 1;

	if (context->NumTextureUnits > 1)
		size += strlen(" GL_ARB_multitexture");

#ifdef MGL_COMPILED_VERTEX_ARRAYS		
	size +=strlen(" GL_EXT_compiled_vertex_arrays");
#endif
	
	if (env_combine != W3D_NOT_SUPPORTED)
		size += strlen(" GL_ARB_texture_env_combine");

	if (env_crossbar != W3D_NOT_SUPPORTED)
		size += strlen(" GL_ARB_texture_env_crossbar");
		
	if (env_add != W3D_NOT_SUPPORTED)
		size += strlen(" GL_ARB_texture_env_add GL_EXT_texture_env_add");

	context->extensionString = IExec->AllocVec(size, MEMF_ANY);
	if (!context->extensionString)
		return;
	
	strcpy(context->extensionString, DEFAULT_EXT);

	if (context->NumTextureUnits > 1)
		strcat(context->extensionString, " GL_ARB_multitexture");

#ifdef MGL_COMPILED_VERTEX_ARRAYS		
	strcat(context->extensionString, " GL_EXT_compiled_vertex_arrays");
#endif
	
	if (env_combine != W3D_NOT_SUPPORTED)
		strcat(context->extensionString, " GL_ARB_texture_env_combine");

	if (env_crossbar != W3D_NOT_SUPPORTED)
		strcat(context->extensionString, " GL_ARB_texture_env_crossbar");
		
	if (env_add != W3D_NOT_SUPPORTED)
		strcat(context->extensionString, " GL_ARB_texture_env_add GL_EXT_texture_env_add");
}

const GLubyte * cgl_GLGetString(struct GLContextIFace *Self, GLenum name)
{	
	GLcontext context = GET_INSTANCE(Self);

	switch(name)
	{
		case GL_RENDERER:
			if (context->w3dContext)
			{
				switch(context->w3dContext->CurrentChip)
				{
					case W3D_CHIP_VIRGE:
						return (GLubyte *)"MiniGL/Warp3D S3 ViRGE (virge)";
					case W3D_CHIP_PERMEDIA2:
						return (GLubyte *)"MiniGL/Warp3D 3DLabs Permedia 2 (permedia)";
					case W3D_CHIP_VOODOO1:
						return (GLubyte *)"MiniGL/Warp3D 3DFX Voodoo 1 (voodoo)";
					case W3D_CHIP_AVENGER:
						return (GLubyte *)"MiniGL/Warp3D 3DFX Voodoo 3 (voodoo avenger)";
					case W3D_CHIP_RADEON:
						return (GLubyte *)"MiniGL/Warp3D ATI Radeon (radeon)";
					case W3D_CHIP_NAPALM:
						return (GLubyte *)"MiniGL/Warp3D 3DFX Voodoo 4 (voodoo napalm)";
					case W3D_CHIP_UNKNOWN:
						return (GLubyte *)"MiniGL/Warp3D Unknown graphics chip";
					default:
						return (GLubyte *)"MiniGL/Warp3D";
				}
			}
			else
			{
				return (GLubyte *)"MiniGL/Warp3D";
			}

		case GL_VENDOR:     
			return (GLubyte *)"The MiniGL Team";
		case GL_VERSION:    
			return (GLubyte *)"1.3";
		case GL_EXTENSIONS: 
			if (!context->extensionString)
				BuildExtensionString(context);
				
			if (context->extensionString)
				return (GLubyte *)context->extensionString;
				
			return (GLubyte *)DEFAULT_EXT;
		default:            
			return (GLubyte *)"Huh?";
	}
}

GLenum cgl_GLGetError(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

	GLenum ret = context->CurrentError;
	context->CurrentError = GL_NO_ERROR;

	return ret;
}


void cgl_GLReadPixels(struct GLContextIFace *Self, GLint x, GLint y, GLsizei width, 
	GLsizei height, GLenum format, GLenum type, GLvoid *pixels)
{
	GLcontext context = GET_INSTANCE(Self);

	struct RastPort rp;
	struct TrueColorInfo ti;
	uint8 *pix = (uint8 *)pixels;
	int i;
	
	// TODO: This implementation is VERY limited!!!
	//if (format != GL_RGB && type != GL_UNSIGNED_BYTE) return;
	
	if (format == GL_RGB)
	{
		ti.PixelDistance = 3;
		ti.BytesPerRow = width*3;
	}
	else
	{
		ti.PixelDistance = 4;
		ti.BytesPerRow = width*4;
	}
	
	bzero(pixels, (width*height*4));
	
	ti.RedData = pix;
	ti.GreenData = pix+1;
	ti.BlueData = pix+2;
	
	IGraphics->InitRastPort(&rp);
	rp.BitMap = context->w3dContext->drawregion;
	
	y = context->w3dContext->height - 1 - y;
	
	for (i=0; i<height; i++)
	{
		IP96->p96ReadTrueColorData(&ti, 0,i,
			&rp/*context->w3dWindow->RPort*/, x, y, width, 1);
		y--;
	}

}

void cgl_SetZOffset(struct GLContextIFace *Self, GLfloat offset)
{
	GLcontext context = GET_INSTANCE(Self);

	context->depth_buffer.ZOffset = offset;
}


void cgl_PinTexture(struct GLContextIFace *Self, GLuint texnum)
{
	GLcontext context = GET_INSTANCE(Self);

#if 0
	W3D_Texture *tex = context->w3dTextures[texnum].texObj;

	if (tex) 
		IWarp3D->W3D_PinTexture(context->w3dContext, tex, TRUE);
	else 
#endif	
		GLFlagError(context, 1, GL_INVALID_OPERATION);
}

void cgl_UnpinTexture(struct GLContextIFace *Self, GLuint texnum)
{
	GLcontext context = GET_INSTANCE(Self);
#if 0
	W3D_Texture *tex = context->w3dTextures[texnum].texObj;
	
	if (tex) 
		IWarp3D->W3D_PinTexture(context->w3dContext, tex, FALSE);
	else 
#endif
		GLFlagError(context, 1, GL_INVALID_OPERATION);
}

void cgl_SetTextureRenderTarget(struct GLContextIFace *Self, GLuint texnum)
{
	GLcontext context = GET_INSTANCE(Self);
#if 0
	if (texnum != 0)
	{
		W3D_Texture *tex = context->w3dTextures[texnum].texObj;
		if (tex)
		{
			context->backupScissor = context->w3dScissor;
			context->w3dScissor.left = 0;
			context->w3dScissor.top = 0;
			context->w3dScissor.width = tex->texwidth;
			context->w3dScissor.height = tex->texheight;
			context->textureRenderTarget = texnum;
			if (IWarp3D->W3D_SetDrawRegionTexture(context->w3dContext,
				tex, &context->w3dScissor) != W3D_SUCCESS)
			{
				GLFlagError(context, 1, GL_INVALID_OPERATION);
			}
				
		} else 
			GLFlagError(context, 1, GL_INVALID_OPERATION);
	}
	else
	{
		context->textureRenderTarget = 0;
		context->w3dScissor = context->backupScissor;
		IWarp3D->W3D_SetDrawRegionTexture(context->w3dContext,
			NULL, NULL);
	}	
#else
	GLFlagError(context, 1, GL_INVALID_OPERATION);
#endif	
}

