/*
 * $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>

extern void TMA_Start(LockTimeHandle *handle);
extern GLboolean TMA_Check(LockTimeHandle *handle);


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

void GLEnableClientState(GLcontext context, GLenum state)
{
	switch (state)
	{
	case GL_TEXTURE_COORD_ARRAY:
		context->ClientState |= GLCS_TEXTURE;
		break;
	case GL_COLOR_ARRAY:
		context->ClientState |= GLCS_COLOR;
		break;
	case GL_VERTEX_ARRAY:
		context->ClientState |= GLCS_VERTEX;
		break;
	default:
		GLFlagError(context, 1, GL_INVALID_ENUM);
		break;
	}
}


void GLDisableClientState(GLcontext context, GLenum state)
{
	switch (state)
	{
		case GL_TEXTURE_COORD_ARRAY:
			context->ClientState &= ~GLCS_TEXTURE;
			break;
		case GL_COLOR_ARRAY:
			context->ClientState &= ~GLCS_COLOR;
			break;
		case GL_VERTEX_ARRAY:
			context->ClientState &= ~GLCS_VERTEX;
			break;
		default:
			GLFlagError(context, 1, GL_INVALID_ENUM);
			break;
	}

}


void GLTexCoordPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
    ULONG err;
    if (type != GL_FLOAT)   GLFlagError(context, 1, GL_INVALID_ENUM);
    if (size != 4)          GLFlagError(context, 1, GL_INVALID_VALUE);
    if (stride == 0) stride = 4*sizeof(W3D_Float);

    err = W3D_TexCoordPointer(context->w3dContext, (void *)pointer, stride, 0,
	sizeof(W3D_Float), 3*sizeof(W3D_Float), W3D_TEXCOORD_NORMALIZED);

    GLFlagError(context, err != W3D_SUCCESS, GL_INVALID_VALUE);
}

void GLColorPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
    int w3dStride;
    ULONG err;
    if (type != GL_UNSIGNED_BYTE && type != GL_FLOAT) GLFlagError(context, 1, GL_INVALID_ENUM);
    if (stride == 0)
    {
	w3dStride = size * (type == GL_UNSIGNED_BYTE?sizeof(GLubyte):sizeof(GLfloat));
    }
    else
    {
	w3dStride = stride;
    }
    err = W3D_ColorPointer(context->w3dContext, (void *)pointer, w3dStride,
	(type == GL_UNSIGNED_BYTE ? W3D_COLOR_UBYTE : W3D_COLOR_FLOAT),
	(size == 3 ? W3D_CMODE_RGB : W3D_CMODE_RGBA), 0);

    GLFlagError(context, err != W3D_SUCCESS, GL_INVALID_VALUE);
}


void GLVertexPointer(GLcontext context, GLint size, GLenum type, GLsizei stride, const GLvoid *pointer)
{
    ULONG err;
    GLFlagError(context, size<3, GL_INVALID_VALUE);
    GLFlagError(context, (type != GL_FLOAT && type != GL_DOUBLE), GL_INVALID_ENUM);

    if (stride == 0)
    {
	stride = size * (type == GL_FLOAT ? sizeof(W3D_Float) : sizeof(W3D_Double));
    }

    err = W3D_VertexPointer(context->w3dContext, (void *)pointer, stride,
	type == GL_FLOAT ? W3D_VERTEX_F_F_F : W3D_VERTEX_D_D_D, 0);
}

inline void PreDraw(GLcontext context)
{
    if (context->LockMode == MGL_LOCK_MANUAL) return;
    else if (context->LockMode == MGL_LOCK_AUTOMATIC) // Automatic: Lock per primitive
    {
	if (W3D_SUCCESS == W3D_LockHardware(context->w3dContext))
	{
		context->w3dLocked = GL_TRUE;
		context->CurrentDraw(context);
		W3D_UnLockHardware(context->w3dContext);
		context->w3dLocked = GL_FALSE;
	}
	else
	{
		printf("Error during LockHardware\n");
	}
    }
    else // Smart: Lock timer based
    {
	if (context->w3dLocked == GL_FALSE)
	{
	    if (W3D_SUCCESS != W3D_LockHardware(context->w3dContext))
	    {
		printf("[glEnd] Error during W3D_LockHardware()\n");
		return; // give up
	    }
	    context->w3dLocked = GL_TRUE;
	    TMA_Start(&(context->LockTime));
	}
    }
}

inline void PostDraw(GLcontext context)
{
    if (context->LockMode == MGL_LOCK_SMART && TMA_Check(&(context->LockTime)) == GL_TRUE)
    {
	// Time to unlock
	W3D_UnLockHardware(context->w3dContext);
	context->w3dLocked = GL_FALSE;
    }
}


void GLDrawElements(GLcontext context, GLenum mode, GLsizei count, GLenum type, const GLvoid *indices)
{
    ULONG err, prim, w3dType;
    switch(mode)
    {
	case GL_POINTS:         prim = W3D_PRIMITIVE_POINTS; break;
	case GL_LINE_STRIP:     prim = W3D_PRIMITIVE_LINESTRIP; break;
	case GL_LINE_LOOP:      prim = W3D_PRIMITIVE_LINELOOP; break;
	case GL_LINES:          prim = W3D_PRIMITIVE_LINES; break;
	case GL_TRIANGLE_STRIP: prim = W3D_PRIMITIVE_TRISTRIP; break;
	case GL_TRIANGLE_FAN:   prim = W3D_PRIMITIVE_TRIFAN; break;
	case GL_TRIANGLES:      prim = W3D_PRIMITIVE_TRIANGLES; break;
	default:
	    GLFlagError(context, 1, GL_INVALID_ENUM);
    }
    switch(type)
    {
	case GL_UNSIGNED_BYTE:      w3dType = W3D_INDEX_UBYTE; break;
	case GL_UNSIGNED_SHORT:     w3dType = W3D_INDEX_UWORD; break;
	case GL_UNSIGNED_INT:       w3dType = W3D_INDEX_ULONG; break;
	default:
	    GLFlagError(context, 1, GL_INVALID_ENUM);
    }

    if (context->w3dTexBuffer[context->CurrentBinding])
	W3D_BindTexture(context->w3dContext, 0, context->w3dTexBuffer[context->CurrentBinding]);

    PreDraw(context);
    err = W3D_DrawElements(context->w3dContext, prim, w3dType, count, indices);
    PostDraw(context);

    GLFlagError(context, err != W3D_SUCCESS, GL_INVALID_VALUE);
}


void GLDrawArrays(GLcontext context, GLenum mode, GLint first, GLsizei count)
{
    ULONG err, prim;
    switch(mode)
    {
	case GL_POINTS:         prim = W3D_PRIMITIVE_POINTS; break;
	case GL_LINE_STRIP:     prim = W3D_PRIMITIVE_LINESTRIP; break;
	case GL_LINE_LOOP:      prim = W3D_PRIMITIVE_LINELOOP; break;
	case GL_LINES:          prim = W3D_PRIMITIVE_LINES; break;
	case GL_TRIANGLE_STRIP: prim = W3D_PRIMITIVE_TRISTRIP; break;
	case GL_TRIANGLE_FAN:   prim = W3D_PRIMITIVE_TRIFAN; break;
	case GL_TRIANGLES:      prim = W3D_PRIMITIVE_TRIANGLES; break;
	default:
	    GLFlagError(context, 1, GL_INVALID_ENUM);
    }

    if (context->w3dTexBuffer[context->CurrentBinding])
	W3D_BindTexture(context->w3dContext, 0, context->w3dTexBuffer[context->CurrentBinding]);

    PreDraw(context);
    err = W3D_DrawArray(context->w3dContext, prim, first, count);
    PostDraw(context);
    
    GLFlagError(context, err != W3D_SUCCESS, GL_INVALID_VALUE);
}
