/*
 * (C) Copyright IBM Corporation 2005
 * All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sub license,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.  IN NO EVENT SHALL
 * IBM,
 * AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
 * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <inttypes.h>
#include <GL/gl.h>
#include "indirect.h"
#include "glxclient.h"
#include "indirect_vertex_array.h"
#include <GL/glxproto.h>

static void
do_vertex_attrib_enable( GLuint index, GLboolean val )
{
    __GLXcontext *gc = __glXGetCurrentContext();
    __GLXattribute * state = (__GLXattribute *)(gc->client_state_private);

    if ( ! __glXSetArrayEnable( state, GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB,
				index, val ) ) {
	__glXSetError(gc, GL_INVALID_ENUM);
    }
}


void __indirect_glEnableVertexAttribArrayARB( GLuint index )
{
    do_vertex_attrib_enable( index, GL_TRUE );
}


void __indirect_glDisableVertexAttribArrayARB( GLuint index )
{
    do_vertex_attrib_enable( index, GL_FALSE );
}


static void
get_parameter( unsigned opcode, unsigned size, GLenum target, GLuint index,
	       void * params )
{
    __GLXcontext * const gc = __glXGetCurrentContext();
    Display * const dpy = gc->currentDpy;
    const GLuint cmdlen = 12;

    if (__builtin_expect(dpy != NULL, 1)) {
        GLubyte const * pc = __glXSetupVendorRequest(gc,
						     X_GLXVendorPrivateWithReply,
						     opcode, cmdlen);

	*((GLenum *)(pc +  0)) = target;
	*((GLuint *)(pc +  4)) = index;
	*((GLuint *)(pc +  8)) = 0;

        (void) __glXReadReply(dpy, size, params, GL_FALSE);
        UnlockDisplay(dpy); SyncHandle();
    }
    return;
}


void __indirect_glGetProgramEnvParameterfvARB( GLenum target, GLuint index,
					       GLfloat * params )
{
    get_parameter( 1296, 4, target, index, params );
}


void __indirect_glGetProgramEnvParameterdvARB( GLenum target, GLuint index,
					       GLdouble * params )
{
    get_parameter( 1297, 8, target, index, params );
}


void __indirect_glGetProgramLocalParameterfvARB( GLenum target, GLuint index,
						 GLfloat * params )
{
    get_parameter( 1305, 4, target, index, params );
}


void __indirect_glGetProgramLocalParameterdvARB( GLenum target, GLuint index,
						 GLdouble * params )
{
    get_parameter( 1306, 8, target, index, params );
}


void __indirect_glGetVertexAttribPointervNV( GLuint index, GLenum pname,
					     GLvoid ** pointer )
{
    __GLXcontext * const gc = __glXGetCurrentContext();
    __GLXattribute * state = (__GLXattribute *)(gc->client_state_private);
 
    if ( pname != GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB ) {
	__glXSetError( gc, GL_INVALID_ENUM );
    }
    
    if ( ! __glXGetArrayPointer( state, GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB,
				 index, pointer ) ) {
	__glXSetError( gc, GL_INVALID_VALUE );
    }
}


/**
 * Get the selected attribute from the vertex array state vector.
 * 
 * \returns
 * On success \c GL_TRUE is returned.  Otherwise, \c GL_FALSE is returned.
 */
static GLboolean
get_attrib_array_data( __GLXattribute * state, GLuint index, GLenum cap,
		       GLintptr * data )
{
    GLboolean retval = GL_FALSE;
    const GLenum attrib = GL_VERTEX_ATTRIB_ARRAY_POINTER_ARB;

    switch( cap ) {
    case GL_VERTEX_ATTRIB_ARRAY_ENABLED_ARB:
	retval = __glXGetArrayEnable( state, attrib, index, data );
	break;

    case GL_VERTEX_ATTRIB_ARRAY_SIZE_ARB:
	retval = __glXGetArraySize( state, attrib, index, data );
	break;

    case GL_VERTEX_ATTRIB_ARRAY_STRIDE_ARB:
	retval = __glXGetArrayStride( state, attrib, index, data );
	break;

    case GL_VERTEX_ATTRIB_ARRAY_TYPE_ARB:
	retval = __glXGetArrayType( state, attrib, index, data );
	break;

    case GL_VERTEX_ATTRIB_ARRAY_NORMALIZED_ARB:
	retval = __glXGetArrayNormalized( state, attrib, index, data );
	break;
    }

    
    return retval;
}


static void get_vertex_attrib( __GLXcontext * gc, unsigned vop,
			       GLuint index, GLenum pname,
			       xReply * reply )
{
    Display * const dpy = gc->currentDpy;
    GLubyte * const pc = __glXSetupVendorRequest(gc,
						 X_GLXVendorPrivateWithReply,
						 vop, 8);
						     
    *((uint32_t *)(pc +  0)) = index;
    *((uint32_t *)(pc +  4)) = pname;

    (void) _XReply( dpy, reply, 0, False );
}


void __indirect_glGetVertexAttribivARB( GLuint index, GLenum pname,
					GLint * params )
{
    __GLXcontext * const gc = __glXGetCurrentContext();
    Display * const dpy = gc->currentDpy;
    __GLXattribute * state = (__GLXattribute *)(gc->client_state_private);
    xGLXSingleReply reply;


    get_vertex_attrib( gc, 1303, index, pname, (xReply *) & reply );

    if ( reply.size != 0 ) {
	if ( ! get_attrib_array_data( state, index, pname, params ) ) {
	    if (reply.size == 1) {
		*params = (GLint) reply.pad3;
	    } 
	    else {
		_XRead(dpy, (void *) params, 4 * reply.size);
	    }
	}
    }

    UnlockDisplay(dpy);
    SyncHandle();
}


void __indirect_glGetVertexAttribfvARB( GLuint index, GLenum pname,
					GLfloat * params )
{
    __GLXcontext * const gc = __glXGetCurrentContext();
    Display * const dpy = gc->currentDpy;
    __GLXattribute * state = (__GLXattribute *)(gc->client_state_private);
    xGLXSingleReply reply;


    get_vertex_attrib( gc, 1302, index, pname, (xReply *) & reply );

    if ( reply.size != 0 ) {
	GLintptr data;


	if ( get_attrib_array_data( state, index, pname, & data ) ) {
	    *params = (GLfloat) data;
	}
	else {
	    if (reply.size == 1) {
		(void) memcpy( params, & reply.pad3, sizeof( GLfloat ) );
	    } 
	    else {
		_XRead(dpy, (void *) params, 4 * reply.size);
	    }
	}
    }

    UnlockDisplay(dpy);
    SyncHandle();
}


void __indirect_glGetVertexAttribdvARB( GLuint index, GLenum pname,
					GLdouble * params )
{
    __GLXcontext * const gc = __glXGetCurrentContext();
    Display * const dpy = gc->currentDpy;
    __GLXattribute * state = (__GLXattribute *)(gc->client_state_private);
    xGLXSingleReply reply;


    get_vertex_attrib( gc, 1301, index, pname, (xReply *) & reply );

    if ( reply.size != 0 ) {
	GLintptr data;


	if ( get_attrib_array_data( state, index, pname, & data ) ) {
	    *params = (GLdouble) data;
	}
	else {
	    if (reply.size == 1) {
		(void) memcpy( params, & reply.pad3, sizeof( GLdouble ) );
	    } 
	    else {
		_XRead(dpy, (void *) params, 8 * reply.size);
	    }
	}
    }

    UnlockDisplay(dpy);
    SyncHandle();
}
