/*
 * $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/timer.h>
#include <stdarg.h>

#include "minigl_vectors.c"

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

extern void EstablishCullState();
extern void GLMatrixInit(GLcontext context);
GLboolean MGLInitContext(GLcontext context);
extern void RebindTextures(GLcontext context);
extern void DisableBlendStage(GLcontext context, uint32 stage);
extern void SetBlendStageState(GLcontext context, uint32 stage);
extern void light_Init(GLcontext context);
extern void light_Recalculate(GLcontext context, GLuint light);
extern GLboolean MGLAllocVertexBuffer(GLcontext context, GLuint size);


extern void tex_FreeTextures(GLcontext context);
extern void TMA_Start(LockTimeHandle *handle);


#define DEBUG(x) IExec->DebugPrintF(x)

static void vid_CloseOrUnlockScreen(GLcontext context)
{
	if (!context->w3dScreen)
		return;
		
	if (context->w3dScreenLocked)
		IIntuition->UnlockPubScreen(NULL, context->w3dScreen);
	else
		IIntuition->CloseScreen(context->w3dScreen);
		
	context->w3dScreen = 0;
}

static void vid_Pointer(GLcontext context, struct Window *window)
{
	if (!context->MousePointer)
		context->MousePointer = IExec->AllocVec(12, MEMF_CLEAR|MEMF_CHIP);

	if (window)
		IIntuition->SetPointer(window, context->MousePointer, 1, 16, 0, 0);
}

static void vid_DeletePointer(GLcontext context, struct Window *window)
{
	if (window)         
		IIntuition->ClearPointer(window);
		
	IExec->FreeVec(context->MousePointer);
	
	context->MousePointer = 0;
}

void cgl_GLScissor(struct GLContextIFace *Self, GLint x, GLint y, GLsizei width, GLsizei height)
{
	GLcontext context = GET_INSTANCE(Self);

	context->scissor.x = x;
	context->scissor.y = y;
	context->scissor.w = width;
	context->scissor.h = height;
	
	if (y < 0)
	{
		height += y;
		y = 0;
	}
	
	if (x < 0)
	{
		width += x;
		x = 0;
	}

	if ((height + y) > context->w3dContext->height)
		height = context->w3dContext->height - y;
		
	if ((width + x) > context->w3dContext->width)
		width = context->w3dContext->width - x;
		
	context->w3dScissor.left = x;
	context->w3dScissor.top = context->w3dContext->height-y-height;
	context->w3dScissor.width = width;
	context->w3dScissor.height = height;
	if (context->enable.ScissorTest)
		IWarp3D->W3D_SetScissor(context->w3dContext, &(context->w3dScissor));
}

static void vid_CloseDisplay(GLcontext context)
{
	int i;

	if (context->w3dContext)
	{
		IWarp3D->W3D_FreeZBuffer(context->w3dContext);
		tex_FreeTextures(context);
		IWarp3D->W3D_DestroyContext(context->w3dContext);
		context->w3dContext = 0;
	}

	if (context->Buffers[0])
	{
		context->Buffers[0]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
		while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[0]));
	}

	for (i=0; i<context->NumBuffers; i++)
	{
		if (context->Buffers[i])
			IIntuition->FreeScreenBuffer(context->w3dScreen, context->Buffers[i]);
		context->Buffers[i] = NULL;
	}

	vid_DeletePointer(context, context->w3dWindow);
	if (context->w3dWindow) 
		IIntuition->CloseWindow(context->w3dWindow);

	vid_CloseOrUnlockScreen(context);

	if (context->DoOpenWorkbench) 
		IIntuition->OpenWorkBench();
}

static GLboolean vid_ReopenDisplay(GLcontext context, int w, int h)
{
	ULONG ModeID, IDCMP;
	int i;
	struct TagItem BestModeTags[] =
	{
		{W3D_BMI_WIDTH,  0},
		{W3D_BMI_HEIGHT, 0},
		{W3D_BMI_DEPTH,  0},
		{TAG_DONE,       0}
	};

	struct TagItem OpenScrTags[] =
	{
		{SA_Height,      0},
		{SA_Width,       0},
		{SA_Depth,       8L},
		{SA_DisplayID,   0},
		{SA_ShowTitle,   FALSE},
		{SA_Draggable,   FALSE},
		{TAG_DONE,       0}
	};

	struct TagItem OpenWinTags[] =
	{
		{WA_CustomScreen,        0},
		{WA_Width,               0},
		{WA_Height,              0},
		{WA_Left,                0},
		{WA_Top,                 0},
		{WA_Title,               (uint32)NULL},
		{WA_Flags,               WFLG_ACTIVATE|WFLG_BORDERLESS|WFLG_BACKDROP|WFLG_REPORTMOUSE|WFLG_RMBTRAP},
		{WA_IDCMP,               0},
		{TAG_DONE,               0}
	};

	if (!context)
	    return GL_FALSE;

	#ifndef NCGXDEBUG
	ULONG flag;

	if (GL_FALSE == sys_MaybeOpenVidLibs())
	{
		return GL_FALSE;
	}
	#endif

	if (context->Buffers[0])
	{
		context->Buffers[0]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
		while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[0]));
	}

	for (i=0; i<context->NumBuffers; i++)
	{
		if (context->Buffers[i])
			IIntuition->FreeScreenBuffer(context->w3dScreen, context->Buffers[i]);
		context->Buffers[i] = NULL;
	}

	IDCMP = context->w3dWindow->IDCMPFlags;

	if (context->w3dWindow) 
		IIntuition->CloseWindow(context->w3dWindow); context->w3dWindow = NULL;
	vid_CloseOrUnlockScreen(context);
	
	BestModeTags[0].ti_Data = (ULONG)w;
	BestModeTags[1].ti_Data = (ULONG)h;
	BestModeTags[2].ti_Data = (ULONG)context->Params.newPixelDepth;

	ModeID = IWarp3D->W3D_BestModeID(BestModeTags);

	if (ModeID == INVALID_ID) return GL_FALSE;

	OpenScrTags[0].ti_Data = (ULONG)w;
	OpenScrTags[1].ti_Data = (ULONG)h;
	OpenScrTags[3].ti_Data = ModeID;

	context->w3dScreen = IIntuition->OpenScreenTagList(NULL, OpenScrTags);

	if (!context->w3dScreen)
	{
		dprintf("Failed to re-open screen\n");
		return GL_FALSE;
	}
	context->w3dScreenLocked = GL_FALSE;

	OpenWinTags[0].ti_Data = (ULONG)context->w3dScreen;
	OpenWinTags[1].ti_Data = (ULONG)context->w3dScreen->Width;
	OpenWinTags[2].ti_Data = (ULONG)context->w3dScreen->Height;
	OpenWinTags[7].ti_Data = IDCMP;

	context->w3dWindow = IIntuition->OpenWindowTagList(NULL, OpenWinTags);

	if (!context->w3dWindow)
	{
		dprintf("Failed to re-open window\n");
		goto Duh;
	}

	context->Buffers[0] = IIntuition->AllocScreenBuffer(context->w3dScreen, NULL, SB_SCREEN_BITMAP);

	if (!context->Buffers[0])
	{
		dprintf("Failed to allocate screen buffer\n");
		goto Duh;
	}


	for (i=1; i<context->Params.newNumberOfBuffers; i++)
	{
		context->Buffers[i] = IIntuition->AllocScreenBuffer(context->w3dScreen, NULL, 0);
		if (!context->Buffers[i]) 
			goto Duh;
	}

	context->BufNr      = 1;                    // The drawing buffer
	IGraphics->SetRGB32(&(context->w3dScreen->ViewPort), 0, 0x7fffffff,0x7fffffff,0x7fffffff);

	for (i=0; i<context->NumBuffers; i++)
	{
		context->Buffers[i]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
		while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[i]));
		IGraphics->EraseRect(context->w3dWindow->RPort, 
			context->w3dWindow->BorderLeft,
			context->w3dWindow->BorderTop,
			context->w3dWindow->Width
					- context->w3dWindow->BorderLeft
					- context->w3dWindow->BorderRight-1,
			context->w3dWindow->Height
					- context->w3dWindow->BorderTop
					- context->w3dWindow->BorderBottom-1);
	}
	context->Buffers[0]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
	while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[0]));


	context->Self->GLScissor(0, 0, w, h);
	
	IWarp3D->W3D_SetDrawRegion(context->w3dContext, 
		context->Buffers[1]->sb_BitMap,
		0, &(context->w3dScissor));
	IWarp3D->W3D_FreeZBuffer(context->w3dContext);
	IWarp3D->W3D_AllocZBuffer(context->w3dContext);
	
	if (context->Params.newStencilBuffer) 
	{	
		uint32 clearval = 0;
		if (W3D_SUCCESS == IWarp3D->W3D_AllocStencilBuffer(context->w3dContext))
		{
			IWarp3D->W3D_ClearStencilBuffer(context->w3dContext, &clearval);
		}
	}
	dprintf("setting pointer\n");
	vid_Pointer(context, context->w3dWindow);
	dprintf("returning GL_TRUE\n");				 
	return GL_TRUE;

Duh:
	dprintf("An error occured - closing down\n");
	vid_CloseDisplay(context);
	return GL_FALSE;
}

static GLboolean vid_OpenDisplay(GLcontext context, int pw, int ph, ULONG id)
{
	ULONG ModeID;
	ULONG CError;
	int i;
	ULONG result;
	int w = 0, h = 0;
	uint32 pad;

	dprintf("context = %p\n", context);
	
	if (!context)
	    return GL_FALSE;

	if (id != MGL_SM_BESTMODE && id != MGL_SM_WINDOWMODE)
	{
		ModeID = id;
		dprintf("Using given mode id = %p\n", ModeID);
	}
	else
	{
		w = pw;
		h = ph;

		dprintf("Trying to determine best mode id\n");
		ModeID = IWarp3D->W3D_BestModeIDTags(
				W3D_BMI_WIDTH,		w,
				W3D_BMI_HEIGHT,		h,
				W3D_BMI_DEPTH,		context->Params.newPixelDepth,
			TAG_DONE);
		dprintf("Mode ID = %p\n", ModeID);
	}

	

	if (ModeID == INVALID_ID)
	    return GL_FALSE;

	pad = w;
	if (w % 64 != 0)
		pad = ((w/64)+1)*64;		
		
	context->w3dScreen = IIntuition->OpenScreenTags(NULL,
					SA_Depth,       8L,
					SA_DisplayID,   ModeID,
					SA_ShowTitle,   FALSE,
					SA_Draggable,   FALSE,
					SA_Width,		pad,
				TAG_DONE);

	context->w3dScreenLocked = GL_FALSE;
	
	if (context->Params.clw == GL_TRUE)
	    context->DoOpenWorkbench = IIntuition->CloseWorkBench();
	else
		context->DoOpenWorkbench = FALSE;

	if (context->w3dScreen && id != MGL_SM_BESTMODE)
	{
		w = context->w3dScreen->Width;
		h = context->w3dScreen->Height;
	}

	if (!context->w3dScreen) 
		return GL_FALSE;

	// We don't include IDCMP flags. The user can change this with
	// a call to ModifyIDCMP.
	context->w3dWindow = IIntuition->OpenWindowTags(NULL,
				WA_CustomScreen,		context->w3dScreen,
				WA_Width,				context->w3dScreen->Width,
				WA_Height,				context->w3dScreen->Height,
				WA_Left,				0,
				WA_Top,					0,
				WA_Title,				NULL,
				WA_Flags,               WFLG_ACTIVATE|WFLG_BORDERLESS
										|WFLG_BACKDROP|WFLG_REPORTMOUSE
										|WFLG_RMBTRAP
										| (context->Params.newCloseGadget == GL_TRUE
											? WFLG_CLOSEGADGET : 0),
			TAG_DONE);

	if (!context->w3dWindow) 
		goto Duh;

	context->Buffers[0] = IIntuition->AllocScreenBuffer(context->w3dScreen, NULL, SB_SCREEN_BITMAP);
	if (!context->Buffers[0])
	{
		dprintf("Error: Can't create screen buffer 0\n");
		goto Duh;
	}
	
	for (i=1; i<context->Params.newNumberOfBuffers; i++)
	{
		context->Buffers[i] = IIntuition->AllocScreenBuffer(context->w3dScreen, NULL, 0);
		if (!context->Buffers[i])
		{
			dprintf("Error: Can't create screen buffer %d\n", i);
			goto Duh;
		}
	}

	context->BufNr      = 1;                    // The drawing buffer
	context->NumBuffers = context->Params.newNumberOfBuffers;   // So we know the limit
	context->DoSync     = GL_TRUE;              // Enable sync'ing

	context->w3dContext = IWarp3D->W3D_CreateContextTags(&CError, 
				W3D_CC_MODEID,			ModeID,
				W3D_CC_BITMAP,			context->Buffers[1]->sb_BitMap,
				W3D_CC_DRIVERTYPE,		W3D_DRIVER_BEST,
				W3D_CC_FAST,			FALSE,
				W3D_CC_YOFFSET,			0,
				W3D_CC_GLOBALTEXENV, 	TRUE,
			TAG_DONE);
			
	if (!context->w3dContext || CError != W3D_SUCCESS)
	{
		switch(CError)
		{
			case W3D_ILLEGALINPUT:
					dprintf("Illegal input to CreateContext function\n");
					break;
			case W3D_NOMEMORY:
					dprintf("Out of memory\n");
					break;
			case W3D_NODRIVER:
					dprintf("No suitable driver found\n");
					break;
			case W3D_UNSUPPORTEDFMT:
					dprintf("Supplied bitmap cannot be handled by Warp3D\n");
					break;
			case W3D_ILLEGALBITMAP:
					dprintf("Supplied bitmap not properly initialized\n");
					break;
			default:
					dprintf("An error has occured... gosh\n");
		}
		goto Duh;
	}

	/*
	** Set up a few initial states
	** We always enable scissoring and dithering, since it looks better
	** on 16 bit displays.
	** We also set shading to smooth (Gouraud).
	*/
	IWarp3D->W3D_SetState(context->w3dContext, W3D_DITHERING,    W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD,      W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_PERSPECTIVE,  W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_MULTITEXTURE, W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_CHROMATEST,	 W3D_DISABLE);

	IGraphics->SetRGB32(&(context->w3dScreen->ViewPort), 0, 0x7fffffff,0x7fffffff,0x7fffffff);


	/*
	** Allocate the ZBuffer.
	*/
	result = IWarp3D->W3D_AllocZBuffer(context->w3dContext);

	switch(result)
	{
		case W3D_NOGFXMEM:      dprintf("No ZBuffer: Memory shortage\n");            break;
		case W3D_NOZBUFFER:     dprintf("No ZBuffer: Operation not supported\n");    break;
		case W3D_NOTVISIBLE:    dprintf("No ZBuffer: Screen is not visible\n");      break;
	}

	/*
	 * Allocate the Stencil buffer
	 */
	if (context->Params.newStencilBuffer) 
	{
		result = IWarp3D->W3D_AllocStencilBuffer(context->w3dContext);
		switch(result)
		{
			case W3D_NOGFXMEM:      	dprintf("No SBuffer: Memory shortage\n");            break;
			case W3D_NOSTENCILBUFFER:   dprintf("No SBuffer: Operation not supported\n");    break;
			case W3D_NOTVISIBLE:    	dprintf("No SBuffer: Screen is not visible\n");      break;
			default:
			{
				uint32 clearval = 0;
				IWarp3D->W3D_ClearStencilBuffer(context->w3dContext, &clearval);
			}
		}
	}
	
	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFER, W3D_DISABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFERUPDATE, W3D_ENABLE);
	IWarp3D->W3D_SetZCompareMode(context->w3dContext, W3D_Z_LESS);


	for (i=0; i<context->NumBuffers; i++)
	{
		context->Buffers[i]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
		while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[i]));
		IGraphics->EraseRect(context->w3dWindow->RPort, 
			context->w3dWindow->BorderLeft,
			context->w3dWindow->BorderTop,
			context->w3dWindow->Width
				- context->w3dWindow->BorderLeft
				- context->w3dWindow->BorderRight-1,
			context->w3dWindow->Height	
				- context->w3dWindow->BorderTop
				- context->w3dWindow->BorderBottom-1);
	}

	context->Buffers[0]->sb_DBufInfo->dbi_SafeMessage.mn_ReplyPort = NULL;
	while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[0]));

	context->Self->GLScissor(0, 0, w,h);

	context->w3dLocked = GL_FALSE;

	vid_Pointer(context, context->w3dWindow); 
					 
	return GL_TRUE;

Duh:
	for (i=0; i<context->Params.newNumberOfBuffers; i++)
	{
		if (context->Buffers[i])
		{
			IIntuition->FreeScreenBuffer(context->w3dScreen, context->Buffers[i]);
			context->Buffers[i] = NULL;
		}
	}

	if (context->w3dWindow)
	{
		IIntuition->CloseWindow(context->w3dWindow);
		context->w3dWindow = NULL;
	}

	vid_CloseOrUnlockScreen(context);

	return GL_FALSE;
}

static void vid_CloseWindow(GLcontext context)
{
	if (!context)
	    return;

	if (context->w3dContext)
	{
		IWarp3D->W3D_FreeZBuffer(context->w3dContext);
		tex_FreeTextures(context);
		IWarp3D->W3D_DestroyContext(context->w3dContext);
		context->w3dContext = 0;
	}

	if (context->w3dWindow)
		IIntuition->CloseWindow(context->w3dWindow);
		
	vid_CloseOrUnlockScreen(context);
		
	if (context->w3dBitMap && !context->w3dBitMapAlien)
		IGraphics->FreeBitMap(context->w3dBitMap);
		
	if (context->w3dRastPort)
		IExec->FreeVec(context->w3dRastPort);
}


static GLboolean vid_OpenWindow(GLcontext context, int w, int h)
{
	ULONG CError;
	ULONG result;

	dprintf("context = %p\n", context);
	
	if (!context)
	    return GL_FALSE;

	context->w3dScreen = IIntuition->LockPubScreen(NULL);
	if (!context->w3dScreen)
		return GL_FALSE;
	
	context->w3dScreenLocked = GL_TRUE;

	// We don't include IDCMP flags. The user can change this with
	// a call to ModifyIDCMP.

	context->w3dWindow = IIntuition->OpenWindowTags(NULL,
				WA_PubScreen,			context->w3dScreen,
				WA_InnerWidth,			w,
				WA_InnerHeight,			h,
				WA_Left,				30,
				WA_Top,					30,
				WA_Title,				"MiniGL Display",
				WA_DragBar,				TRUE,
				WA_DepthGadget,			TRUE,
				WA_Flags,				WFLG_ACTIVATE|WFLG_REPORTMOUSE|
										WFLG_RMBTRAP
										| (context->Params.newCloseGadget == GL_TRUE
											? WFLG_CLOSEGADGET : 0)
										| (context->Params.newSizeGadget == GL_TRUE
											? WFLG_SIZEGADGET : 0),
				WA_MinWidth,			100,
				WA_MinHeight,			100,
				WA_MaxWidth,			2048,
				WA_MaxHeight,			2048,
			TAG_DONE);

	dprintf("context->w3dWindow = %p\n", context->w3dWindow);

	if (!context->w3dWindow) 
		goto Duh;

	context->BufNr      = 0;                    // The drawing buffer
	context->NumBuffers = 0;                    // Indicates we're using a window
	context->DoSync     = GL_TRUE;              // Enable sync'ing

	dprintf("Allocating bitmap\n");
	
	context->w3dBitMap  = IGraphics->AllocBitMap(w,h,8,BMF_MINPLANES|BMF_DISPLAYABLE,
		context->w3dWindow->RPort->BitMap);
		
	if (!context->w3dBitMap) 
		goto Duh;

	context->w3dRastPort = IExec->AllocVec(sizeof(struct RastPort), MEMF_ANY);
	if (!context->w3dRastPort)
	{
		dprintf("Error: unable to allocate rastport memory\n");
		goto Duh;
	}

	IGraphics->InitRastPort(context->w3dRastPort);
	context->w3dRastPort->BitMap = context->w3dBitMap;

	dprintf("Creating Warp3D context\n");
	
	context->w3dContext = IWarp3D->W3D_CreateContextTags(&CError, 
				W3D_CC_BITMAP,			context->w3dBitMap,
				W3D_CC_DRIVERTYPE,		W3D_DRIVER_BEST,
				W3D_CC_FAST,			TRUE,
				W3D_CC_YOFFSET,			0,
				W3D_CC_GLOBALTEXENV, 	TRUE,
			TAG_DONE);

	dprintf("context->w3dContext = %p\n", context->w3dContext);
	
	if (!context->w3dContext || CError != W3D_SUCCESS)
	{
		switch(CError)
		{
			case W3D_ILLEGALINPUT:
					dprintf("Illegal input to CreateContext function\n");
					break;
			case W3D_NOMEMORY:
					dprintf("Out of memory\n");
					break;
			case W3D_NODRIVER:
					dprintf("No suitable driver found\n");
					break;
			case W3D_UNSUPPORTEDFMT:
					dprintf("Supplied bitmap cannot be handled by Warp3D\n");
					break;
			case W3D_ILLEGALBITMAP:
					dprintf("Supplied bitmap not properly initialized\n");
					break;
			default:
					dprintf("An error has occured... gosh\n");
		}
		goto Duh;
	}
	
	/*
	** Set up a few initial states
	** We always enable scissoring and dithering, since it looks better
	** on 16 bit displays.
	** We also set shading to smooth (Gouraud).
	*/
	dprintf("Setting up default states\n");
	IWarp3D->W3D_SetState(context->w3dContext, W3D_DITHERING,    W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD,      W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_PERSPECTIVE,  W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_MULTITEXTURE, W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_CHROMATEST,	 W3D_DISABLE);

	/*
	** Allocate the ZBuffer.
	*/
	dprintf("Allocating depth buffer\n");
	result = IWarp3D->W3D_AllocZBuffer(context->w3dContext);
	switch(result)
	{
		case W3D_NOGFXMEM:      dprintf("No ZBuffer: Memory shortage\n"); break;
		case W3D_NOZBUFFER:     dprintf("No ZBuffer: Operation not supported\n"); break;
		case W3D_NOTVISIBLE:    dprintf("No ZBuffer: Screen is not visible\n"); break;
	}

	/*
	 * Allocate the Stencil buffer
	 */
	dprintf("Allocating stencil buffer if needed\n");
	if (context->Params.newStencilBuffer) 
	{
		dprintf("Allocating stencil buffer\n");
		result = IWarp3D->W3D_AllocStencilBuffer(context->w3dContext);
		switch(result)
		{
			case W3D_NOGFXMEM:      	dprintf("No SBuffer: Memory shortage\n");            break;
			case W3D_NOSTENCILBUFFER:   dprintf("No SBuffer: Operation not supported\n");    break;
			case W3D_NOTVISIBLE:    	dprintf("No SBuffer: Screen is not visible\n");      break;
			default:
			{
				uint32 clearval = 0;
				IWarp3D->W3D_LockHardware(context->w3dContext);
				IWarp3D->W3D_ClearStencilBuffer(context->w3dContext, &clearval);
				IWarp3D->W3D_UnLockHardware(context->w3dContext);
			}
		}
	}
	dprintf("Setting a few initial states\n");

	dprintf("Setting Z modes\n");
	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFER, W3D_DISABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFERUPDATE, W3D_ENABLE);
	IWarp3D->W3D_SetZCompareMode(context->w3dContext, W3D_Z_LESS);
	
	dprintf("Erasing window rectangle\n");

	dprintf("Clearing window rectangle\n");
	IGraphics->EraseRect(context->w3dWindow->RPort, 
		context->w3dWindow->BorderLeft,
		context->w3dWindow->BorderTop,
		context->w3dWindow->Width
			- context->w3dWindow->BorderLeft
			- context->w3dWindow->BorderRight-1,
		context->w3dWindow->Height
			- context->w3dWindow->BorderTop
			- context->w3dWindow->BorderBottom-1);

	dprintf("Calling GLScissor\n");
	context->Self->GLScissor(0, 0, w, h);

	dprintf("dum dee dum\n");
	context->Self->LockMode(MGL_LOCK_SMART);
	context->w3dLocked = GL_FALSE;

	dprintf("Success\n");
	return GL_TRUE;

Duh:
	dprintf("Failure\n");
	if (context->w3dWindow)
	{
		IIntuition->CloseWindow(context->w3dWindow);
		context->w3dWindow = NULL;
	}

	if (context->w3dScreen)
	{
		IIntuition->UnlockPubScreen(NULL, context->w3dScreen);
		context->w3dScreen = NULL;
	}

	if (context->w3dBitMap && !context->w3dBitMapAlien)
	{
		IGraphics->FreeBitMap(context->w3dBitMap);
	}

	if (context->w3dRastPort)
	{
		IExec->FreeVec(context->w3dRastPort);
	}
	dprintf("Error, returning GL_FALSE\n");
	return GL_FALSE;
}


static GLboolean vid_SetBitmap(GLcontext context, struct BitMap *bitmap, int *w, int *h)
{
	ULONG CError;
	ULONG result;

	if (!context)
	    return GL_FALSE;

	context->BufNr      = 0;                    // The drawing buffer
	context->NumBuffers = 0;                    // Indicates we're using a window/bitmap
	context->DoSync     = GL_TRUE;              // Enable sync'ing

	context->w3dBitMap  = bitmap;
	context->w3dBitMapAlien = GL_TRUE;
		
	if (!context->w3dBitMap) 
		goto Duh;

	context->w3dRastPort = IExec->AllocVec(sizeof(struct RastPort), MEMF_ANY);
	if (!context->w3dRastPort)
	{
		dprintf("Error: unable to allocate rastport memory\n");
		goto Duh;
	}

	IGraphics->InitRastPort(context->w3dRastPort);
	context->w3dRastPort->BitMap = context->w3dBitMap;

	context->w3dContext = IWarp3D->W3D_CreateContextTags(&CError,
				W3D_CC_BITMAP, 			context->w3dBitMap,
				W3D_CC_DRIVERTYPE,		W3D_DRIVER_BEST,
				W3D_CC_FAST,			TRUE,
				W3D_CC_YOFFSET,			0,
				W3D_CC_GLOBALTEXENV,	TRUE,
			TAG_DONE);
			
	if (!context->w3dContext || CError != W3D_SUCCESS)
	{
		switch(CError)
		{
			case W3D_ILLEGALINPUT:
					dprintf("Illegal input to CreateContext function\n");
					break;
			case W3D_NOMEMORY:
					dprintf("Out of memory\n");
					break;
			case W3D_NODRIVER:
					dprintf("No suitable driver found\n");
					break;
			case W3D_UNSUPPORTEDFMT:
					dprintf("Supplied bitmap cannot be handled by Warp3D\n");
					break;
			case W3D_ILLEGALBITMAP:
					dprintf("Supplied bitmap not properly initialized\n");
					break;
			default:
					dprintf("An error has occured... gosh\n");
		}
		goto Duh;
	}
	/*
	** Set up a few initial states
	** We always enable scissoring and dithering, since it looks better
	** on 16 bit displays.
	** We also set shading to smooth (Gouraud).
	*/
	IWarp3D->W3D_SetState(context->w3dContext, W3D_DITHERING,    W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_GOURAUD,      W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_PERSPECTIVE,  W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_MULTITEXTURE, W3D_ENABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_CHROMATEST,	 W3D_DISABLE);

	/*
	** Allocate the ZBuffer.
	*/
	result = IWarp3D->W3D_AllocZBuffer(context->w3dContext);
	switch(result)
	{
		case W3D_NOGFXMEM:      dprintf("No ZBuffer: Memory shortage\n"); break;
		case W3D_NOZBUFFER:     dprintf("No ZBuffer: Operation not supported\n"); break;
		case W3D_NOTVISIBLE:    dprintf("No ZBuffer: Screen is not visible\n"); break;
	}

	/*
	 * Allocate the Stencil buffer
	 */
	if (context->Params.newStencilBuffer) 
	{
		result = IWarp3D->W3D_AllocStencilBuffer(context->w3dContext);
		switch(result)
		{
			case W3D_NOGFXMEM:      	dprintf("No SBuffer: Memory shortage\n");            break;
			case W3D_NOSTENCILBUFFER:   dprintf("No SBuffer: Operation not supported\n");    break;
			case W3D_NOTVISIBLE:    	dprintf("No SBuffer: Screen is not visible\n");      break;
			default:
			{
				uint32 clearval = 0;
				IWarp3D->W3D_ClearStencilBuffer(context->w3dContext, &clearval);
			}
		}
	}

	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFER, W3D_DISABLE);
	IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFERUPDATE, W3D_ENABLE);
	IWarp3D->W3D_SetZCompareMode(context->w3dContext, W3D_Z_LESS);

	*w = IP96->p96GetBitMapAttr(bitmap, P96BMA_WIDTH);
	*h = IP96->p96GetBitMapAttr(bitmap, P96BMA_HEIGHT);
	
	context->Self->GLScissor(0, 0, *w, *h);

	context->w3dLocked = GL_FALSE;

	return GL_TRUE;

Duh:
	if (context->w3dRastPort)
	{
		IExec->FreeVec(context->w3dRastPort);
	}

	return GL_FALSE;
}


void cgl_ResizeContext(struct GLContextIFace *Self, GLsizei width, GLsizei height)
{
	GLcontext context = GET_INSTANCE(Self);

	uint32 result;
	
	if (!context->w3dBitMap)
	{
		vid_ReopenDisplay(context, (int)width, (int)height);
		context->w3dChipID = context->w3dContext->CurrentChip;
	}
	else
	{
		IGraphics->FreeBitMap(context->w3dBitMap);
		context->w3dBitMap  = IGraphics->AllocBitMap(width,height,8,BMF_MINPLANES|BMF_DISPLAYABLE,
						context->w3dWindow->RPort->BitMap);
						
		IGraphics->InitRastPort(context->w3dRastPort);
		context->w3dRastPort->BitMap = context->w3dBitMap;
		
		IWarp3D->W3D_SetDrawRegion(context->w3dContext, context->w3dBitMap,
			0, &(context->w3dScissor));
		/*
		** Allocate the ZBuffer.
		*/
		IWarp3D->W3D_FreeZBuffer(context->w3dContext);
		result = IWarp3D->W3D_AllocZBuffer(context->w3dContext);
		switch(result)
		{
			case W3D_NOGFXMEM:      dprintf("No ZBuffer: Memory shortage\n"); break;
			case W3D_NOZBUFFER:     dprintf("No ZBuffer: Operation not supported\n"); break;
			case W3D_NOTVISIBLE:    dprintf("No ZBuffer: Screen is not visible\n"); break;
		}

		/*
		 * Allocate the Stencil buffer
		 */
		if (context->Params.newStencilBuffer) 
		{
			IWarp3D->W3D_FreeStencilBuffer(context->w3dContext);
			result = IWarp3D->W3D_AllocStencilBuffer(context->w3dContext);
			switch(result)
			{
				case W3D_NOGFXMEM:      	dprintf("No SBuffer: Memory shortage\n");            break;
				case W3D_NOSTENCILBUFFER:   dprintf("No SBuffer: Operation not supported\n");    break;
				case W3D_NOTVISIBLE:    	dprintf("No SBuffer: Screen is not visible\n");      break;
				default:
				{
					uint32 clearval = 0;
					IWarp3D->W3D_ClearStencilBuffer(context->w3dContext, &clearval);
				}
			}
		}

		RebindTextures(context);
	}
}

void *cgl_GetWindowHandle(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

	return context->w3dWindow;
}

void cgl_GLClearStencil(struct GLContextIFace *Self, GLint s)
{
	GLcontext context = GET_INSTANCE(Self);

	context->stencil_buffer.ClearStencil = s;
}

void cgl_GLClearColor(struct GLContextIFace *Self, GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)
{
	GLcontext context = GET_INSTANCE(Self);
	
	context->color_buffer.ClearColorRec.r = red;
	context->color_buffer.ClearColorRec.g = green;
	context->color_buffer.ClearColorRec.b = blue;
	context->color_buffer.ClearColorRec.a = alpha;
#if 0
	red *= 255.0;
	green *= 255.0;
	blue *= 255.0;
	alpha *= 255.0;

	context->ClearColor = ((GLubyte)(alpha)<<24)
						+ ((GLubyte)(red)<<16)
						+ ((GLubyte)(green)<<8)
						+ ((GLubyte)(blue));
#endif
}

void cgl_GLDepthMask(struct GLContextIFace *Self, GLboolean flag)
{
	GLcontext context = GET_INSTANCE(Self);

	if (flag == GL_FALSE)
		IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFERUPDATE, W3D_DISABLE);
	else
		IWarp3D->W3D_SetState(context->w3dContext, W3D_ZBUFFERUPDATE, W3D_ENABLE);
	context->depth_buffer.DepthWriteMask = flag;
}

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

	ULONG wFunc = W3D_Z_LESS;

	switch(func)
	{
		case GL_NEVER:      wFunc = W3D_Z_NEVER; break;
		case GL_LESS:       wFunc = W3D_Z_LESS; break;
		case GL_EQUAL:      wFunc = W3D_Z_EQUAL; break;
		case GL_LEQUAL:     wFunc = W3D_Z_LEQUAL; break;
		case GL_GREATER:    wFunc = W3D_Z_GREATER; break;
		case GL_NOTEQUAL:   wFunc = W3D_Z_NOTEQUAL; break;
		case GL_GEQUAL:     wFunc = W3D_Z_GEQUAL; break;
		case GL_ALWAYS:     wFunc = W3D_Z_ALWAYS; break;
		default:
			GLFlagError(context, 1, GL_INVALID_ENUM);
			break;
	}
	IWarp3D->W3D_SetZCompareMode(context->w3dContext, wFunc);
	context->depth_buffer.DepthFunc = func;
}


void cgl_GLClearDepth(struct GLContextIFace *Self, GLclampd depth)
{
	GLcontext context = GET_INSTANCE(Self);

	context->depth_buffer.ClearDepth = (W3D_Double)depth;
}

void cgl_GLClear(struct GLContextIFace *Self, GLbitfield mask)
{
	GLcontext context = GET_INSTANCE(Self);

	if (context->w3dLocked == GL_FALSE)
		IWarp3D->W3D_LockHardware(context->w3dContext);

	W3D_Color *color = NULL;
	W3D_Double *depth = NULL;
	uint32 *stencil = NULL;
	
	if (mask & GL_COLOR_BUFFER_BIT) color = 
		&(context->color_buffer.ClearColorRec);
	if (mask & GL_DEPTH_BUFFER_BIT) depth = 
		&(context->depth_buffer.ClearDepth);
	if (mask & GL_STENCIL_BUFFER_BIT) stencil = 
		(uint32 *)&(context->stencil_buffer.ClearStencil);
	IWarp3D->W3D_ClearBuffers(context->w3dContext, color, depth, stencil);
	
	if (context->w3dLocked == GL_FALSE)
		IWarp3D->W3D_UnLockHardware(context->w3dContext);
}

#ifdef AUTOMATIC_LOCKING_ENABLE
void cgl_LockMode(struct GLContextIFace *Self, GLenum lockMode)
{
	GLcontext context = GET_INSTANCE(Self);

	context->LockMode = lockMode;
}
#endif

GLboolean cgl_LockBack(struct GLContextIFace *Self, MGLLockInfo *info)
{
	GLcontext context = GET_INSTANCE(Self);

	ULONG error;
	
	if (context->w3dLocked == GL_TRUE)
	{
		error = W3D_SUCCESS;
	}
	else
	{
		error = IWarp3D->W3D_LockHardware(context->w3dContext);
	}

	if (error != W3D_SUCCESS)
	{
		return GL_FALSE;
	}
	else
	{
		context->w3dLocked = GL_TRUE;
		TMA_Start(&(context->LockTime));
		if (info)
		{
			info->width = context->w3dContext->width;
			info->height = context->w3dContext->height;
			info->depth = context->w3dContext->depth;
			info->pixel_format = context->w3dContext->format;
			info->base_address = context->w3dContext->drawmem;
			info->pitch = context->w3dContext->bprow;
		}
	}
	return GL_TRUE;
}

GLboolean cgl_LockDisplay(struct GLContextIFace *Self)
{
	ULONG error;
	GLcontext context = GET_INSTANCE(Self);

	if (context->w3dLocked == GL_TRUE) 
		return GL_TRUE; // nothing to do if we are already locked

	switch(context->LockMode)
	{
		case MGL_LOCK_MANUAL:
			error = IWarp3D->W3D_LockHardware(context->w3dContext);
			if (error == W3D_SUCCESS)
			{
				context->w3dLocked = GL_TRUE;
				return GL_TRUE;
			}
			break;
		case MGL_LOCK_AUTOMATIC: // These modes do not require the lock right here.
		case MGL_LOCK_SMART:
			return GL_TRUE;
			break;
	}
	
	dprintf("[MGLLockDisplay] Unable to lock\n");
	return GL_FALSE;            // If we got here, there was an error
}

void cgl_UnlockDisplay(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

	if (context->w3dLocked == GL_FALSE)
		return;
		
	switch(context->LockMode)
	{
		case MGL_LOCK_AUTOMATIC:
			break;
		case MGL_LOCK_SMART:
		case MGL_LOCK_MANUAL:
			IWarp3D->W3D_UnLockHardware(context->w3dContext);
			break;
	}
	
	context->w3dLocked = GL_FALSE;
}

void cgl_EnableSync(struct GLContextIFace *Self, GLboolean enable)
{
	GLcontext context = GET_INSTANCE(Self);

	context->DoSync = enable;
}

#if 0
void cgl_EnableFrameStats(GLcontext context, GLboolean enable)
{
	context->enableFPS = enable;
	if (enable)
	{
		context->statFrame = 0;
		context->currentFPS = 0.0;
		context->averageFPS = 0.0;
		context->accumulatedFPS = 0.0;
		context->currentFrameTime = 0.0;
		context->averageFrameTime = 0.0;
		context->accumulatedFrameTime = 0.0;
		context->currentSwapTime = 0.0;
		context->averageSwapTime = 0.0;
		context->accumulatedSwapTime = 0.0;
	}
}
#endif

void cgl_SwitchDisplay(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

	int nowbuf = context->BufNr;
	Self->UnlockDisplay();
	
	if (context->statFrame != 0 && context->enableFPS)
	{
		struct TimeVal sub;
		
		if (!context->w3dBitMap)
		{
			char buffer[512];
			/* Display last frame's data */
			IUtility->SNPrintf(buffer, 510, "[%4.4f/%4.4f fps] [%.2f/%.2f µs] [%.2f/%.2f µs]      ",
				context->currentFPS, context->averageFPS,
				context->currentFrameTime, context->averageFrameTime,
				context->currentSwapTime, context->averageSwapTime);
			IGraphics->SetAPen(context->w3dWindow->RPort, 2);
			IGraphics->SetBPen(context->w3dWindow->RPort, 0);
			IGraphics->SetDrMd(context->w3dWindow->RPort, JAM1);
			IGraphics->Move(context->w3dWindow->RPort, 
  							context->w3dWindow->BorderRight+10,
        	            	context->w3dWindow->BorderTop+10);
			IGraphics->Text(context->w3dWindow->RPort, buffer, strlen(buffer));
		}
			
		ITimer->GetSysTime(&context->frameEndTime);
		
		sub = context->frameEndTime;
		ITimer->SubTime(&sub, &context->frameStartTime);
		context->currentFrameTime = sub.Microseconds 
			+ 1000000.0 * sub.Seconds;
	}
	
	if (!context->w3dBitMap) // Fullscreen mode
	{
		context->BufNr++;
		if (context->BufNr == context->NumBuffers)
			context->BufNr = 0;

		/*
		** At this place, nowbuf contains the buffer we where drawing to.
		** BufNr is the next drawing buffer.
		** We switch nowbof to the be the display buffer, and set
		** the draw region to the new BufNr
		*/

		// FIXME: This may cause a context switch orgy, maybe make it 68k
		// Make nowbuf current display

		while (!IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[nowbuf]));
		
//		IIntuition->ChangeScreenBuffer(context->w3dScreen, context->Buffers[nowbuf]);
		
		// Make BufNr the new draw area
		IWarp3D->W3D_SetDrawRegion(context->w3dContext, context->Buffers[context->BufNr]->sb_BitMap,
			0, &(context->w3dScissor));

		if (context->DoSync)
		{
			struct ViewPort *vp = &(context->w3dScreen->ViewPort);
			IGraphics->WaitBOVP(vp);
		}
	}
	else
	{   // Windowed mode
		IGraphics->ClipBlit(context->w3dRastPort,0,0,
			context->w3dWindow->RPort, 
			context->w3dWindow->BorderLeft,
			context->w3dWindow->BorderTop,
			context->w3dWindow->Width
				- context->w3dWindow->BorderLeft
				- context->w3dWindow->BorderRight,
			context->w3dWindow->Height
				- context->w3dWindow->BorderTop
				- context->w3dWindow->BorderBottom,
			0xC0);
			
		if (context->GrabMouse == GL_TRUE)
		{
		    struct IBox b;
		    b.Left   = context->w3dWindow->BorderLeft;
		    b.Top    = context->w3dWindow->BorderTop;
		    b.Width  = context->w3dWindow->Width 
		             - context->w3dWindow->BorderLeft 
		             - context->w3dWindow->BorderRight;
		    b.Height = context->w3dWindow->Height
		             - context->w3dWindow->BorderTop
		             - context->w3dWindow->BorderBottom;
		    IIntuition->SetWindowAttrs(context->w3dWindow,
		        WA_GrabFocus, 100,
		        WA_MouseLimits, &b,
		    TAG_DONE);
        }
	}
	
	if (context->enableFPS)
	{
		struct TimeVal diff;
		
		/* Start time for new frame */
		ITimer->GetSysTime(&context->frameStartTime);
		
		/* frameStartTime - frameEndTime is the time used for swapping the
		 * buffer
		 */
		diff = context->frameStartTime;
		ITimer->SubTime(&diff, &context->frameEndTime);
		context->currentSwapTime = diff.Microseconds
			 + 1000000.0 * diff.Seconds;
		
		if (context->statFrame != 0)
		{
			/* Calculate current fps */
			context->currentFPS = (float) ((double)1000000.0 
									/ (double)context->currentFrameTime);
			
			context->accumulatedFPS += context->currentFPS;
			context->averageFPS = context->accumulatedFPS / (float)context->statFrame;
			
			context->accumulatedFrameTime += context->currentFrameTime;
			context->averageFrameTime = context->accumulatedFrameTime / (float)context->statFrame;
			
			context->accumulatedSwapTime += context->currentSwapTime;
			context->averageSwapTime = context->accumulatedSwapTime / (float)context->statFrame;

			if (context->w3dBitMap)
			{
				char buffer[512];

				IUtility->SNPrintf(buffer, 510, "[%4.4f/%4.4f fps] [%.2f/%.2f µs] [%.2f/%.2f µs]      ",
					context->currentFPS, context->averageFPS,
					context->currentFrameTime, context->averageFrameTime,
					context->currentSwapTime, context->averageSwapTime);
				IGraphics->SetAPen(context->w3dWindow->RPort, 2);
				IGraphics->SetBPen(context->w3dWindow->RPort, 0);
				IGraphics->SetDrMd(context->w3dWindow->RPort, JAM1);
				IGraphics->Move(context->w3dWindow->RPort, 
  							context->w3dWindow->BorderRight+10,
        	            	context->w3dWindow->BorderTop+10);
				IGraphics->Text(context->w3dWindow->RPort, buffer, strlen(buffer));
			}
		}
		else
		{
			context->averageFPS = context->currentFPS;
			context->averageFrameTime = context->currentFrameTime;
			context->averageSwapTime = context->averageSwapTime;
			context->accumulatedFPS = context->currentFPS;
			context->accumulatedFrameTime = context->currentFrameTime;
			context->accumulatedSwapTime = context->averageSwapTime;
			
		}
		
		context->statFrame++;
		
	}		
}

void cgl_GrabFocus(struct GLContextIFace *Self, GLboolean yesno)
{
	GLcontext context = GET_INSTANCE(Self);

    context->GrabMouse = yesno;

    if (yesno == GL_FALSE)
    {
        IIntuition->SetWindowAttrs(context->w3dWindow,
            WA_GrabFocus, 0,
            WA_MouseLimits, NULL,
        TAG_DONE);
    }
}

GLboolean MGLEnsureVertexBufferSize(GLcontext context, GLuint size)
{
	void *oldvb;
	uint32 oldSize;
	
	if (size <= context->VertexBufferSize)
		return GL_TRUE;
		
	/* Need to enlarge */
	oldvb = context->VertexBuffer;
	oldSize = context->VertexBufferSize;
	
	if (MGLAllocVertexBuffer(context, size))
	{
		/* Got a new one, copy the old one */
		memcpy(context->VertexBuffer, oldvb, oldSize);
		IExec->FreeVec(oldvb);
		return GL_TRUE;
	}

	/* If we get here, it didn'T work. Put the old one back */
	context->VertexBuffer = oldvb;
	context->VertexBufferSize = oldSize;
	
	return GL_FALSE;
}
	
GLboolean MGLAllocVertexBuffer(GLcontext context, GLuint size)
{
	int i;
	
	context->VertexBuffer       = IExec->AllocVec(sizeof(MGLVertex)*size, MEMF_ANY);
	if (!context->VertexBuffer)
		return GL_FALSE;
	
	context->VertexBufferSize   = (GLuint)size;
	context->VertexBufferPointer= 0;
	
	/* Set frame codes */
	for (i = 0; i < context->VertexBufferSize; i++)
	{
		context->VertexBuffer[i].frame_code = 0xffffffff;
		context->VertexBuffer[i].tex_frame_code = 0xffffffff;
	}
	
	return GL_TRUE;
}
	
GLboolean MGLInitContext(GLcontext context)
{
	int i;
	char buffer[10];
	GLuint clipMask;

    context->GrabMouse = GL_FALSE;
	context->CurrentPrimitive   = GL_BASE;
	context->CurrentError       = GL_NO_ERROR;

	if (!MGLAllocVertexBuffer(context, context->Params.newVertexBufferSize))
		return GL_FALSE;
		
	context->IndexBuffer		= IExec->AllocVec(sizeof(GLuint) * context->Params.newVertexBufferSize, MEMF_ANY);
	if (!context->IndexBuffer)
		return GL_FALSE;

	context->TexBufferSize      = context->Params.newTextureBufferSize;
	context->GeneratedTextures  = IExec->AllocVec(sizeof(GLubyte) * context->Params.newTextureBufferSize, MEMF_ANY);

	if (!context->GeneratedTextures)
		return GL_FALSE;

	context->w3dTextures       = IExec->AllocVec(sizeof(MGLTexture) * context->Params.newTextureBufferSize, MEMF_ANY);
	if (!context->w3dTextures)
		return GL_FALSE;

	for (i=0; i<context->Params.newTextureBufferSize; i++)
	{
		context->w3dTextures[i].texObj  = NULL;
		context->w3dTextures[i].texData = NULL;
		context->GeneratedTextures[i]   = 0;
	}

	context->NumTextureUnits = IWarp3D->W3D_Query(context->w3dContext, W3D_Q_NUM_TMU, 0);
	
	for (i = 0; i < context->NumTextureUnits; i++)
	{
		context->current.CurTexS[i] 		= 0.0;
		context->current.CurTexT[i] 		= 0.0;
		context->current.CurTexQ[i] 		= 1.0;
		context->current.CurTexQValid[i] 	= GL_FALSE;
		context->enable.Texture2D[i]    	= GL_FALSE;
		context->texture.TexGenS[i].mode	= GL_EYE_LINEAR;
		context->texture.TexGenT[i].mode	= GL_EYE_LINEAR;
		context->enable.TexGenS[i]			= GL_FALSE;
		context->enable.TexGenT[i]			= GL_FALSE;
		context->texture.TexGenS[i].objectPlane[0] = 1.0;
		context->texture.TexGenS[i].objectPlane[1] = 0.0;
		context->texture.TexGenS[i].objectPlane[2] = 0.0;
		context->texture.TexGenS[i].objectPlane[3] = 0.0;
		context->texture.TexGenT[i].objectPlane[0] = 0.0;
		context->texture.TexGenT[i].objectPlane[1] = 1.0;
		context->texture.TexGenT[i].objectPlane[2] = 0.0;
		context->texture.TexGenT[i].objectPlane[3] = 0.0;		
		context->texture.TextureEnv[i] 		= GL_MODULATE;
		context->texture.CurrentBinding[i]	= 0;
		IWarp3D->W3D_SetTextureBlendTags(context->w3dContext,
					W3D_BLEND_STAGE, 	i,
					W3D_ENV_MODE,		W3D_OFF,
					TAG_DONE);
	}

	context->texture.MinFilter		= GL_NEAREST;
	context->texture.MagFilter		= GL_NEAREST;
	context->texture.WrapS			= GL_REPEAT;
	context->texture.WrapT			= GL_REPEAT;

	/* Multitexturing */
	context->texture.ActiveTexture 		= 0;
	context->texture.MaxTextureUnit 	= -1;

	/* Initialize texture env combine values */
	for (i=0; i<MAX_TEXTURE_UNITS; i++)
	{
		context->combineDirty[i] = GL_TRUE;
		context->texture.colorCombine[i] = GL_MODULATE;
		context->texture.alphaCombine[i] = GL_MODULATE;
		
		context->texture.colorSource[0][i] = GL_TEXTURE;
		context->texture.colorSource[1][i] = GL_PREVIOUS;
		context->texture.colorSource[2][i] = GL_CONSTANT;
	
		context->texture.alphaSource[0][i] = GL_TEXTURE;
		context->texture.alphaSource[1][i] = GL_PREVIOUS;
		context->texture.alphaSource[2][i] = GL_CONSTANT;
		
		context->texture.colorOperand[0][i] = GL_SRC_COLOR;
		context->texture.colorOperand[1][i] = GL_SRC_COLOR;
		context->texture.colorOperand[2][i] = GL_SRC_ALPHA;
		
		context->texture.alphaOperand[0][i] = GL_SRC_ALPHA;
		context->texture.alphaOperand[1][i] = GL_SRC_ALPHA;
		context->texture.alphaOperand[2][i] = GL_SRC_ALPHA;
		
		context->texture.envColor[i].r = 0.0f;
		context->texture.envColor[i].g = 0.0f;
		context->texture.envColor[i].b = 0.0f;
		context->texture.envColor[i].a = 0.0f;
		
		context->texture.colorScale[i] = 1;
		context->texture.alphaScale[i] = 1;
	}
	
	context->ConvertTo16Bit = GL_FALSE;
	context->ConvertBuffer = 0;
	context->ConvertBufferSize = 0;

	context->FogDirty				= GL_FALSE;
	context->current.CurrentFogDepth= 1.0;
	context->fog.CurrentFogSource 	= GL_FRAGMENT_DEPTH;
	context->fog.FogStart			= 1.0;
	context->fog.FogEnd				= 0.0;

	context->w3dFog.fog_start   = 1.0;
	context->w3dFog.fog_end     = 0.1;
	context->w3dFog.fog_density = 1.0;
	context->w3dFog.fog_color.r = 0.0;
	context->w3dFog.fog_color.g = 0.0;
	context->w3dFog.fog_color.b = 0.0;
	context->FogRange           = 1.0;
	
	context->enable.AlphaTest		= GL_FALSE;
	context->enable.Blend			= GL_FALSE;
	context->enable.Fog				= GL_FALSE;
	context->enable.ScissorTest		= GL_FALSE;
	context->enable.CullFace		= GL_FALSE;
	context->enable.DepthTest		= GL_FALSE;
	context->enable.PointSmooth		= GL_FALSE;
	context->enable.Dither			= GL_TRUE;
	context->enable.ZOffset			= GL_FALSE;
	context->enable.PolygonOffsetFill = GL_FALSE;

	context->polygon.CullFace		= GL_BACK;
	context->polygon.FrontFace		= GL_CCW;
	context->polygon.PolygonOffsetFactor = 0.0;
	context->polygon.PolygonOffsetUnits = 0.0;
	
	context->lighting.ShadeModel	= GL_SMOOTH;

	context->depth_buffer.DepthWriteMask = GL_TRUE;
	context->depth_buffer.ClearDepth= 1.0;
	context->depth_buffer.ZOffset = 0.0;
	
#ifdef AUTOMATIC_LOCKING_ENABLE
	context->LockMode = MGL_LOCK_MANUAL;
#endif

	context->NoMipMapping = context->Params.newNoMipMapping;
	context->NoFallbackAlpha = GL_FALSE; //newNoFallbackAlpha;

	context->color_buffer.BlendSrc	= GL_ONE;
	context->color_buffer.BlendDst	= GL_ZERO;
	
	context->AlphaFellBack = GL_FALSE;

	context->WOne_Hint = GL_FALSE;

	context->PaletteData = IExec->AllocVec(4*256, MEMF_ANY);
	context->PaletteSize = 0;
	context->PaletteFormat = 0;

	context->polygon.PolygonModeFront	= GL_FILL;
	context->polygon.PolygonModeBack	= GL_FILL;

	context->pixel_store.UnpackRowLength	= 0 ;
	context->pixel_store.UnpackSkipPixels	= 0 ;
	context->pixel_store.UnpackSkipRows		= 0 ;
	context->pixel_store.PackAlign			= 4;
	context->pixel_store.UnpackAlign		= 4;
	context->pixel_store.UnpackLSBFirst		= GL_FALSE;


	/* Vertex array stuff */
	context->vertex_array.ClientState		= 0;
	context->vertex_array.DrawElementsHook	= 0;
	context->vertex_array.DrawArraysHook	= 0;
	context->vertex_array.ClientActiveTexture= 0;

	/* Area: All triangles smaller than this will not be drawn */
	context->MinTriArea         = 0.5f;

	context->VertexFormat		= W3D_VFORMAT_FOG 
								| W3D_VFORMAT_COLOR 
								| W3D_VFORMAT_SCOLOR 
								| W3D_VFORMAT_TCOORD_0
								| W3D_VFORMAT_TCOORD_1
								| W3D_VFORMAT_TCOORD_2;
									
	context->FrameCode = 0;
	context->TexFrameCode = 0;

	context->statFrame = 0;
	context->currentFPS = 0.0;
	context->averageFPS = 0.0;
	context->accumulatedFPS = 0.0;
	context->currentFrameTime = 0.0;
	context->averageFrameTime = 0.0;
	context->accumulatedFrameTime = 0.0;
	context->currentSwapTime = 0.0;
	context->averageSwapTime = 0.0;
	context->accumulatedSwapTime = 0.0;
	
	IDOS->GetVar("MiniGL/FrameStats", buffer, 10, 0);
	if (strcmp(buffer, "1") == 0 || strcasecmp(buffer, "on") == 0)
	{
		dprintf("Showing frame statistics\n");
		context->enableFPS = GL_TRUE;
	}
	else
	{
		dprintf("Suppressing frame statistics\n");
		context->enableFPS = GL_FALSE;			
	}
	
	GLMatrixInit(context);
	light_Init(context);
	
	if (IWarp3D->W3D_Query(context->w3dContext, W3D_Q_ENV_COMBINE, 0) ==
		W3D_NOT_SUPPORTED)
	{
		context->envCombineSupported = GL_FALSE;
	}
	else
	{
		context->envCombineSupported = GL_TRUE;
	}
	

	context->extensionString = 0;
	
	/* FIXME: Find a way to really determine this */
	context->DepthBufferUnit = 1/65536.0;
	
	context->LockArraysFirst = 0;
	context->LockArraysCount = 0;
	context->LockedArrays = 0;
	context->TransformFrameCode = 0;
	
	/* Initialize clipplane states */
	context->transform.MaxClipPlane = -1;
	clipMask = MGL_CLIP_USER0;
	for (i = 0; i < MAX_CLIPPLANES; i++)
	{
		context->enable.ClipPlane[i] = GL_FALSE;
		context->transform.UserClipPlane[i].outcode = clipMask;
		clipMask <<= 1;
	}

	context->line.LineWidth = 1.0f;
	context->point.PointSize = 1.0f;

	/* Initialize raster position */
	context->current.RasterPos.x = 0.0f;
	context->current.RasterPos.y = 0.0f;
	context->current.RasterPos.z = 0.0f;
	context->current.RasterPos.w = 1.0f;
	context->current.RasterDistance = 0.0f;
	context->current.RasterColor.r = 1.0f;
	context->current.RasterColor.g = 1.0f;
	context->current.RasterColor.b = 1.0f;
	context->current.RasterColor.a = 1.0f;

	for (i = 0; i < MAX_TEXTURE_UNITS; i++)
	{
		context->current.RasterTexCoords[i].u = 0.0;
		context->current.RasterTexCoords[i].v = 0.0;
		context->current.RasterTexCoords[i].w = 1.0;
		context->current.RasterRhwValid[i] = GL_TRUE;
	}

	context->current.RasterPosValid = GL_TRUE;

	/* glBitmap texture backing store */
	context->CurrentBitmapBackingStoreSize = 0;
	context->CurrentBitmapBackingStore = 0;
	
	/* Texture Render Target */
	context->textureRenderTarget = 0;
	
	context->NeedEye = GL_FALSE;
	context->NeedEyeNormal = GL_FALSE;
	return GL_TRUE;
}



void cgl_DeleteContext(struct GLContextIFace *Self)
{
	GLcontext context = GET_INSTANCE(Self);

//	GLint current,peak;

	if (context->w3dBitMap && !context->w3dBitMapAlien) vid_CloseWindow(context);
	else                    vid_CloseDisplay(context);

	if (context->VertexBuffer)      IExec->FreeVec(context->VertexBuffer);
	if (context->IndexBuffer)		IExec->FreeVec(context->IndexBuffer);
	if (context->w3dTextures)       IExec->FreeVec(context->w3dTextures);
	if (context->GeneratedTextures) IExec->FreeVec(context->GeneratedTextures);
	if (context->ConvertBuffer)		IExec->FreeVec(context->ConvertBuffer);
	if (context->extensionString)	IExec->FreeVec(context->extensionString);
	if (context->CurrentBitmapBackingStore)
									IExec->FreeVec(context->CurrentBitmapBackingStore);
	if (context->PaletteData)		IExec->FreeVec(context->PaletteData);


//	IExec->FreeVec(context);
	IExec->DeleteInterface((struct Interface *)Self);
}



void cgl_SetBitmap(struct GLContextIFace *Self, void *bitmap)
{
	GLcontext context = GET_INSTANCE(Self);

	IWarp3D->W3D_SetDrawRegion(context->w3dContext, (struct BitMap *)bitmap, 
		0, &(context->w3dScissor));
}




void cgl_MinTriArea(struct GLContextIFace *Self, GLfloat area)
{
	GLcontext context = GET_INSTANCE(Self);

	context->MinTriArea = area;
}

/* ************************************************************************** */

GLint mgl_GetSupportedScreenModes(struct MiniGLIFace *IMiniGL, 
				MGLScreenModeCallback CallbackFn)
{
	W3D_ScreenMode *Modes = IWarp3D->W3D_GetScreenmodeList();
	W3D_ScreenMode *Cursor;
	MGLScreenMode   sMode;
	GLboolean       retval;

	if (Modes == NULL)
		return MGL_SM_BESTMODE;

	Cursor = Modes;
	while (Cursor)
	{
		sMode.id        = (GLint)Cursor->ModeID;
		sMode.width     = (GLint)Cursor->Width;
		sMode.height    = (GLint)Cursor->Height;
		sMode.bit_depth = (GLint)Cursor->Depth;
		strncpy(sMode.mode_name, Cursor->DisplayName, MGL_MAX_MODE);
		
		retval = CallbackFn(&sMode);
		
		if (retval == GL_TRUE)
		{
			IWarp3D->W3D_FreeScreenmodeList(Modes);
			return sMode.id;
		}
		
		Cursor = Cursor->Next;
	}
	
	IWarp3D->W3D_FreeScreenmodeList(Modes);
	return MGL_SM_BESTMODE;
}


void *mgl_CreateContext(struct MiniGLIFace *IMiniGL, struct TagItem *tagList)
{
	GLcontext context;
	BOOL bWindowMode;
	void *pBitmap;
	int32 width, height, offx, offy;
	uint32 mode;
	struct GLContextIFace *IGL;
	int w, h;
		
	/* First of all, create the interface */
	IGL = (struct GLContextIFace *)IExec->MakeInterfaceTags(IMiniGL->Data.LibBase,
				MIT_VectorTable,	miniglcontext_vectors,
				MIT_DataSize,		sizeof(struct GLcontext_t),
				MIT_Flags,			IFLF_NONE,
				MIT_Version,		1,
				MIT_Name,			"MiniGLContext",
			TAG_DONE);
	
	if (!IGL)
		return 0;
		
	context = (GLcontext)GET_INSTANCE(IGL);
	
	IUtility->ClearMem(context, sizeof(struct GLcontext_t));
	
	width = (int32)IUtility->GetTagData(MGLCC_Width, (uint32)-1, tagList);
	height = (int32)IUtility->GetTagData(MGLCC_Height, (uint32)-1, tagList);
	offx = (int32)IUtility->GetTagData(MGLCC_OffsetX, 0, tagList);
	offy = (int32)IUtility->GetTagData(MGLCC_OffsetY, 0, tagList);
	bWindowMode = (BOOL)IUtility->GetTagData(MGLCC_Windowed, (uint32)FALSE, tagList);
	pBitmap = (void *)IUtility->GetTagData(MGLCC_Bitmap, 0, tagList);
	mode = IUtility->GetTagData(MGLCC_ScreenMode, MGL_SM_BESTMODE, tagList);

	dprintf("width = %ld, height = %ld, offx = %ld, offy = %ld\n", 
			width, height, offx, offy);
	
	context->Params.newVertexBufferSize = IUtility->GetTagData(MGLCC_VertexBufferSize, 8192, tagList);
	context->Params.newTextureBufferSize = IUtility->GetTagData(MGLCC_TextureBufferSize, 8192, tagList);
	context->Params.newNumberOfBuffers = IUtility->GetTagData(MGLCC_Buffers, 3, tagList);
	context->Params.newPixelDepth = IUtility->GetTagData(MGLCC_PixelDepth, 16, tagList);
	context->Params.newWindowMode = bWindowMode;
	context->Params.clw = (GLboolean)IUtility->GetTagData(MGLCC_CloseWorkbench, GL_FALSE, tagList);
	context->Params.newNoMipMapping = (GLboolean)IUtility->GetTagData(MGLCC_NoMipMapping, GL_FALSE, tagList);
	context->Params.newCloseGadget = (GLboolean)IUtility->GetTagData(MGLCC_CloseGadget, GL_FALSE, tagList);
	context->Params.newStencilBuffer = (GLboolean)IUtility->GetTagData(MGLCC_StencilBuffer, GL_FALSE, tagList);
	context->Params.newSizeGadget = (GLboolean)IUtility->GetTagData(MGLCC_SizeGadget, GL_FALSE, tagList);
	
	context->Self = IGL;
	
	dprintf("%ld screen buffers\n", context->Params.newNumberOfBuffers);

	/* If we have a bitmap, use it */
	if (pBitmap)
	{
		dprintf("Using supplied bitmap\n");
		if (!vid_SetBitmap(context, (struct BitMap *)pBitmap, &w, &h))
		{
			dprintf("Error: Can't set bitmap\n");
			IExec->DeleteInterface((struct Interface *)IGL);
			
			return NULL;
		}
	}
	else if (bWindowMode || mode == MGL_SM_WINDOWMODE)
	{
		/* Window mode */
		dprintf("Using window mode\n");
		if (GL_FALSE == vid_OpenWindow(context, width, height))
		{
			vid_CloseWindow(context);
			IExec->DeleteInterface((struct Interface *)IGL);
			dprintf("Error: opening of display failed\n");

			return NULL;
		}
	}
	else
	{
		/* Fullscreen mode */
		dprintf("Using fullscreen mode\n");
		if (GL_FALSE == vid_OpenDisplay(context, width, height, mode))
		{
			vid_CloseDisplay(context);
			IExec->DeleteInterface((struct Interface *)IGL);
			dprintf("Error: opening of display failed\n");

			return NULL;
		}
	}
	
	switch(context->w3dContext->CurrentChip)
	{
	case W3D_CHIP_RADEON:
	case W3D_CHIP_RADEON_R200:
	case W3D_CHIP_RADEON_R300:
		context->UseZFog = GL_TRUE;
		break;
	default:
		context->UseZFog = GL_FALSE;
	}
	
	dprintf("Initializing context\n");
	
	/* Initialize the context */
	if (GL_FALSE == MGLInitContext(context))
	{
		dprintf("Error: Can't initialize context\n");
		IGL->DeleteContext();
		
		return NULL;
	}
	
	dprintf("Setting up default parameters\n");
	IGL->GLDepthRange(0.0, 1.0);
	IGL->GLViewport(offx, offy, width, height);
	IGL->GLClearColor(0.0, 0.0, 0.0, 1.0);
	
	dprintf("Done\n");
	return (void *)IGL;
}

void *mgl_CreateContextTags(struct MiniGLIFace *IMiniGL, ...)
{
	va_list ap;
    void *res;
	struct TagItem *tags;

	va_startlinear(ap, IMiniGL);
	tags = va_getlinearva(ap, struct TagItem *);
	res = IMiniGL->CreateContext(tags);

	return res;
}


void checkNeedEye(GLcontext context)
{
	int i;
	
	context->NeedEye = GL_FALSE;
	context->NeedEyeNormal = GL_FALSE;

	/* Check if we need eye coordinates and eye normals
	 * 
	 * Currently, this is true when the following is true:
	 * - lighting is enabled
	 * - texture coordinate generation is enabled for modes that need eye
	 *   coordinates
	 * - User clip planes are enabled (coordinates only)
	 */
	
	if (context->enable.Lighting == GL_TRUE)
	{
		context->NeedEye = GL_TRUE;
		context->NeedEyeNormal = GL_TRUE;
		return;
	}
	
	for (i = 0; i <= context->texture.MaxTextureUnit; i++)
	{
		if (context->enable.TexGenT[i] == GL_TRUE 
		 || context->enable.TexGenS[i] == GL_TRUE)
		{
			/* FIXME: Check for mode */
			context->NeedEye = GL_TRUE;
			context->NeedEyeNormal = GL_TRUE;
			return;
		}
	}
	
	for (i = 0; i <= context->transform.MaxClipPlane; i++)
	{
		if (context->enable.ClipPlane[i])
		{
			context->NeedEye = GL_TRUE;
			return;
		}
	}
	
#if 1
	// TODO: This should not be needed, since only the eyez is required for
	//		 fogging. Try to pull this out.
	if (context->enable.Fog == GL_TRUE)
	{
		context->NeedEye = GL_TRUE;
	}
#endif	
}
