"""Implementations for "held-pointers" of various types

This argument type is special because it is stored, that is, it 
needs to be cached on our side so that the memory address does not
go out-of-scope

storedPointers = {}
def glVertexPointerd( array ):
	"Natural writing of glVertexPointerd using standard ctypes"
	arg2 = GL_DOUBLE
	arg3 = 0 # stride
	arg4 = arrays.asArray(array, GL_DOUBLE)
	arg1 = arrays.arraySize( arg4, 'd' )
	platform.OpenGL.glVertexPointer( arg1, arg2, arg3, arrays.ArrayDatatype.dataPointer(arg4) )
	glCheckError()
	# only store if we successfully set the value...
	storedPointers[ GL_VERTEX_ARRAY ] = arg4
	return arg4
"""
from OpenGL import platform, arrays, error, wrapper, contextdata, converters, constant
from OpenGL.raw import GL as simple
from OpenGL.raw.GL import annotations
import ctypes
import weakref

GLsizei = ctypes.c_int
GLenum = ctypes.c_uint
GLint = ctypes.c_int
# OpenGL-ctypes variables that mimic OpenGL constant operation...
GL_INTERLEAVED_ARRAY_POINTER = constant.Constant( 'GL_INTERLEAVED_ARRAY_POINTER', -32910 )

__all__ = (
	'glColorPointer',
	'glColorPointerb','glColorPointerd','glColorPointerf','glColorPointeri',
	'glColorPointers','glColorPointerub','glColorPointerui','glColorPointerus',
	'glEdgeFlagPointer',
	'glEdgeFlagPointerb',
	'glIndexPointer',
	'glIndexPointerb','glIndexPointerd','glIndexPointerf',
	'glIndexPointeri','glIndexPointers','glIndexPointerub',
	'glNormalPointer',
	'glNormalPointerb',
	'glNormalPointerd','glNormalPointerf','glNormalPointeri','glNormalPointers',
	'glTexCoordPointer',
	'glTexCoordPointerb','glTexCoordPointerd','glTexCoordPointerf',
	'glTexCoordPointeri','glTexCoordPointers',
	'glVertexPointer',
	'glVertexPointerb','glVertexPointerd','glVertexPointerf','glVertexPointeri',
	'glVertexPointers',
	'glDrawElements','glDrawElementsui','glDrawElementsub','glDrawElementsus',
	'glFeedbackBuffer',
	'glSelectBuffer',
	'glRenderMode',
	'glGetPointerv',
	'glInterleavedArrays',
	'GL_INTERLEAVED_ARRAY_POINTER',
)


# Have to create *new* ctypes wrappers for the platform object!
# We can't just alter the default one since we have different ways of
# calling it

POINTER_FUNCTION_DATA = [
	('glColorPointerd',  simple.glColorPointer, simple.GL_DOUBLE, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	('glColorPointerf',  simple.glColorPointer, simple.GL_FLOAT, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	('glColorPointeri',  simple.glColorPointer, simple.GL_INT, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	('glColorPointers',  simple.glColorPointer, simple.GL_SHORT, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	('glColorPointerub', simple.glColorPointer, simple.GL_UNSIGNED_BYTE, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	# these data-types are mapped from diff Numeric types
	('glColorPointerb',  simple.glColorPointer, simple.GL_BYTE, simple.GL_COLOR_ARRAY_POINTER, 0, 3), 
	('glColorPointerui', simple.glColorPointer, simple.GL_UNSIGNED_INT, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	('glColorPointerus', simple.glColorPointer, simple.GL_UNSIGNED_SHORT, simple.GL_COLOR_ARRAY_POINTER, 0, 3),
	
	('glEdgeFlagPointerb', simple.glEdgeFlagPointer, simple.GL_BYTE, simple.GL_EDGE_FLAG_ARRAY_POINTER, 2, None),

	('glIndexPointerd',  simple.glIndexPointer, simple.GL_DOUBLE, simple.GL_INDEX_ARRAY_POINTER, 1, None),
	('glIndexPointerf',  simple.glIndexPointer, simple.GL_FLOAT, simple.GL_INDEX_ARRAY_POINTER, 1, None),
	('glIndexPointeri',  simple.glIndexPointer, simple.GL_INT, simple.GL_INDEX_ARRAY_POINTER, 1, None),
	('glIndexPointerub', simple.glIndexPointer, simple.GL_UNSIGNED_BYTE, simple.GL_INDEX_ARRAY_POINTER, 1, None),
	('glIndexPointers',  simple.glIndexPointer, simple.GL_SHORT, simple.GL_INDEX_ARRAY_POINTER, 1, None),
	# these data-types are mapped from diff Numeric types
	('glIndexPointerb',  simple.glIndexPointer, simple.GL_BYTE, simple.GL_INDEX_ARRAY_POINTER, 1, None),

	('glNormalPointerd',  simple.glNormalPointer, simple.GL_DOUBLE, simple.GL_NORMAL_ARRAY_POINTER, 1, None),
	('glNormalPointerf',  simple.glNormalPointer, simple.GL_FLOAT, simple.GL_NORMAL_ARRAY_POINTER, 1, None),
	('glNormalPointeri',  simple.glNormalPointer, simple.GL_INT, simple.GL_NORMAL_ARRAY_POINTER, 1, None),
	('glNormalPointerb',  simple.glNormalPointer, simple.GL_BYTE, simple.GL_NORMAL_ARRAY_POINTER, 1, None),
	('glNormalPointers',  simple.glNormalPointer, simple.GL_SHORT, simple.GL_NORMAL_ARRAY_POINTER, 1, None),

	('glTexCoordPointerd',  simple.glTexCoordPointer, simple.GL_DOUBLE, simple.GL_TEXTURE_COORD_ARRAY_POINTER, 0, 2),
	('glTexCoordPointerf',  simple.glTexCoordPointer, simple.GL_FLOAT, simple.GL_TEXTURE_COORD_ARRAY_POINTER, 0, 2),
	('glTexCoordPointeri',  simple.glTexCoordPointer, simple.GL_INT, simple.GL_TEXTURE_COORD_ARRAY_POINTER, 0, 2),
	('glTexCoordPointerb',  simple.glTexCoordPointer, simple.GL_BYTE, simple.GL_TEXTURE_COORD_ARRAY_POINTER, 0, 2),
	('glTexCoordPointers',  simple.glTexCoordPointer, simple.GL_SHORT, simple.GL_TEXTURE_COORD_ARRAY_POINTER, 0, 2),

	('glVertexPointerd', simple.glVertexPointer, simple.GL_DOUBLE, simple.GL_VERTEX_ARRAY_POINTER, 0, 3),
	('glVertexPointerf', simple.glVertexPointer, simple.GL_FLOAT, simple.GL_VERTEX_ARRAY_POINTER, 0, 3),
	('glVertexPointeri', simple.glVertexPointer, simple.GL_INT, simple.GL_VERTEX_ARRAY_POINTER, 0, 3),
	('glVertexPointerb', simple.glVertexPointer, simple.GL_INT, simple.GL_VERTEX_ARRAY_POINTER, 0, 3),
	('glVertexPointers', simple.glVertexPointer, simple.GL_SHORT, simple.GL_VERTEX_ARRAY_POINTER, 0, 3),
]
def wrapPointerFunction( name, baseFunction, glType, arrayType,startArgs, defaultSize ):
	"""Wrap the given pointer-setting function"""
	function= wrapper.wrapper( baseFunction )
	assert not getattr( function, 'pyConverters', None ), """Reusing wrappers?"""
	if arrayType:
		arrayType = arrays.GL_CONSTANT_TO_ARRAY_TYPE[ glType ]
		function.setPyConverter( 'pointer', arrays.asArrayType(arrayType) )
		function.setCResolver( 'pointer', arrayType.voidDataPointer )
	else:
		function.setPyConverter( 'pointer', arrays.AsArrayOfType('pointer','type') )
		function.setCResolver( 'pointer', arrays.ArrayDatatype.voidDataPointer )
	function.setCConverter( 'pointer', converters.getPyArgsName( 'pointer' ) )
	if 'size' in function.argNames:
		function.setPyConverter( 'size' )
		function.setCConverter( 'size', arrays.arraySizeOfFirstType(arrayType,defaultSize) )
	if 'type' in function.argNames:
		function.setPyConverter( 'type' )
		function.setCConverter( 'type', glType )
	if 'stride' in function.argNames:
		function.setPyConverter( 'stride' )
		function.setCConverter( 'stride', 0 )
	function.setStoreValues( arrays.storePointerType( 'pointer', arrayType ) )
	function.setReturnValues( wrapper.returnPyArgument( 'pointer' ) )
	return name,function



for name,function in [
	wrapPointerFunction( *args )
	for args in POINTER_FUNCTION_DATA
]:
	globals()[name] = function
del name, function

glVertexPointer = wrapper.wrapper( simple.glVertexPointer ).setPyConverter(
	'pointer', arrays.AsArrayOfType( 'pointer', 'type' ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_VERTEX_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glTexCoordPointer = wrapper.wrapper( simple.glTexCoordPointer ).setPyConverter(
	'pointer', arrays.AsArrayOfType( 'pointer', 'type' ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_TEXTURE_COORD_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glNormalPointer = wrapper.wrapper( simple.glNormalPointer ).setPyConverter(
	'pointer', arrays.AsArrayOfType( 'pointer', 'type' ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_NORMAL_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glIndexPointer = wrapper.wrapper( simple.glIndexPointer ).setPyConverter(
	'pointer', arrays.AsArrayOfType( 'pointer', 'type' ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_INDEX_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glEdgeFlagPointer = wrapper.wrapper( simple.glEdgeFlagPointer ).setPyConverter(
	# XXX type is wrong!
	'pointer', arrays.AsArrayTyped( 'pointer', arrays.GLushortArray ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_EDGE_FLAG_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glColorPointer = wrapper.wrapper( simple.glColorPointer ).setPyConverter(
	'pointer', arrays.AsArrayOfType( 'pointer', 'type' ),
).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', simple.GL_COLOR_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' ) 
)
glInterleavedArrays = wrapper.wrapper( simple.glInterleavedArrays ).setCResolver( 
	'pointer', arrays.ArrayDatatype.voidDataPointer ,
).setStoreValues( 
	arrays.storePointerType( 'pointer', GL_INTERLEAVED_ARRAY_POINTER ) 
).setReturnValues( 
	wrapper.returnPyArgument( 'pointer' )
)
	

glDrawElements = wrapper.wrapper( simple.glDrawElements ).setPyConverter(
	'indices', arrays.AsArrayOfType( 'indices', 'type' ),
).setCResolver( 
	'indices', arrays.ArrayDatatype.voidDataPointer ,
).setReturnValues( 
	wrapper.returnPyArgument( 'indices' ) 
)

def glDrawElementsTyped( type, suffix ):
	arrayType = arrays.GL_CONSTANT_TO_ARRAY_TYPE[ type ]
	function = wrapper.wrapper( 
		simple.glDrawElements 
	).setPyConverter('type').setCConverter(
		'type', type
	).setPyConverter('count').setCConverter(
		'count', arrays.AsArrayTypedSize( 'indices', arrayType ),
	).setPyConverter(
		'indices', arrays.AsArrayTyped( 'indices', arrayType ),
	).setCResolver( 
		'indices', arrayType.voidDataPointer ,
	).setReturnValues(
		wrapper.returnPyArgument( 'indices' )
	)
	return function
for type,suffix in ((simple.GL_UNSIGNED_BYTE,'ub'),(simple.GL_UNSIGNED_INT,'ui'),(simple.GL_UNSIGNED_SHORT,'us')):
	globals()['glDrawElements%(suffix)s'%globals()] = glDrawElementsTyped( type,suffix )
del type,suffix,glDrawElementsTyped

# create buffer of given size and return it for future reference
# keep a per-context weakref around to allow us to return the original
# array we returned IFF the user has kept a reference as well...
def glSelectBuffer( size, buffer = None ):
	"""Create a selection buffer of the given size
	"""
	if buffer is None:
		buffer = arrays.GLintArray.zeros( (size,) )
	simple.glSelectBuffer( size, buffer )
	contextdata.setValue( simple.GL_SELECTION_BUFFER_POINTER, buffer )
	return buffer
def glFeedbackBuffer( size, type, buffer = None ):
	"""Create a selection buffer of the given size
	"""
	if buffer is None:
		buffer = arrays.GLfloatArray.zeros( (size,) )
	simple.glFeedbackBuffer( size, type, buffer )
	contextdata.setValue( simple.GL_FEEDBACK_BUFFER_POINTER, buffer )
	contextdata.setValue( "GL_FEEDBACK_BUFFER_TYPE", type )
	return buffer

def glRenderMode( newMode ):
	"""Change to the given rendering mode
	
	If the current mode is GL_FEEDBACK or GL_SELECT, return
	the current buffer appropriate to the mode
	"""
	# must get the current mode to determine operation...
	from OpenGL.GL import glGetIntegerv
	from OpenGL.GL import selection, feedback
	currentMode = glGetIntegerv( simple.GL_RENDER_MODE )
	if currentMode in (simple.GL_RENDER,0):
		# no array needs to be returned...
		return simple.glRenderMode( newMode )
	result = simple.glRenderMode( newMode )
	# result is now an integer telling us how many elements were copied...
	
	if result < 0:
		if currentMode == simple.GL_SELECT:
			raise error.GLError(
				simple.GL_STACK_OVERFLOW,
				"glSelectBuffer too small to hold selection results",
			)
		elif currentMode == simple.GL_FEEDBACK:
			raise error.GLError(
				simple.GL_STACK_OVERFLOW,
				"glFeedbackBuffer too small to hold selection results",
			)
		else:
			raise error.GLError(
				simple.GL_STACK_OVERFLOW,
				"Unknown glRenderMode buffer (%s) too small to hold selection results"%(
					currentMode,
				),
			)
	# Okay, now that the easy cases are out of the way...
	#  Do we have a pre-stored pointer about which the user already knows?
	context = platform.GetCurrentContext()
	if context == 0:
		raise error.Error(
			"""Returning from glRenderMode without a valid context!"""
		)
	arrayConstant, wrapperFunction = {
		simple.GL_FEEDBACK: (simple.GL_FEEDBACK_BUFFER_POINTER,feedback.parseFeedback),
		simple.GL_SELECT: (simple.GL_SELECTION_BUFFER_POINTER, selection.GLSelectRecord.fromArray),
	}[ currentMode ]
	current = contextdata.getValue( arrayConstant )
	# XXX check to see if it's the *same* array we set currently!
	if current is None:
		current = glGetPointerv( arrayConstant )
	# XXX now, can turn the array into the appropriate wrapper type...
	if wrapperFunction:
		current = wrapperFunction( current, result )
	return current

# XXX this belongs in the GL module, not here!
def glGetPointerv( constant ):
	"""Retrieve a stored pointer constant"""
	# do we have a cached version of the pointer?
	# get the base pointer from the underlying operation
	vp = ctypes.voidp()
	simple.glGetPointerv( constant, ctypes.byref(vp) )
	current = contextdata.getValue( constant )
	if current is not None:
		if arrays.ArrayDatatype.dataPointer( current ) == vp.value:
			return current
	# XXX should be coercing to the proper type and converting to an array
	return vp
