#! /usr/bin/env python
"""Test for a buffer-protocol-based access mechanism

Will *only* work for Python 2.6+, and pretty much just works for strings
under 2.6 (in terms of the common object types).
"""
import ctypes,sys,operator,logging,traceback
from OpenGL.arrays import _buffers
from OpenGL.raw.GL import _types
from OpenGL.raw.GL.VERSION import GL_1_1
from OpenGL.arrays import formathandler
from OpenGL import _configflags
from OpenGL import acceleratesupport
_log = logging.getLogger( __name__ )
try:
    reduce 
except NameError as err:
    from functools import reduce

MemoryviewHandler = BufferHandler = None
if sys.version_info[:2] > (2,6):
    # Only Python 2.7+ has memoryview support, and the accelerate module 
    # requires memoryviews to be able to pass around the buffer structures
    if acceleratesupport.ACCELERATE_AVAILABLE:
        try:
            from OpenGL_accelerate.buffers_formathandler import MemoryviewHandler
        except ImportError as err:
            traceback.print_exc()
            _log.warn(
                "Unable to load buffers_formathandler accelerator from OpenGL_accelerate"
            )
        else:
            BufferHandler = MemoryviewHandler
if not BufferHandler:
    class BufferHandler( formathandler.FormatHandler ):
        """Buffer-protocol data-type handler for OpenGL"""
        isOutput=False
        ERROR_ON_COPY = _configflags.ERROR_ON_COPY
        if sys.version_info[:2] >= (3,0):
            @classmethod
            def from_param( cls, value, typeCode=None ):
                if not isinstance( value, _buffers.Py_buffer ):
                    value = cls.asArray( value )
                    #raise TypeError( """Can't convert value to py-buffer in from_param""" )
                return value
        else:
            # Not sure *why* 2.7 segfaults with the "use a buffer as the buffer"
            # version above, but it works fine with the value.buf here
            @classmethod
            def from_param( cls, value, typeCode=None ):
                if not isinstance( value, _buffers.Py_buffer ):
                    value = cls.asArray( value )
                    #raise TypeError( """Can't convert value to py-buffer in from_param""" )
                return value.buf
        def dataPointer( value ):
            if not isinstance( value, _buffers.Py_buffer ):
                value = _buffers.Py_buffer.from_object( value )
            return value.buf
        dataPointer = staticmethod( dataPointer )
        @classmethod
        def zeros( cls, dims, typeCode=None ):
            """Currently don't allow strings as output types!"""
            raise NotImplementedError( "Generic buffer type does not have output capability" )
            return cls.asArray( bytearray( b'\000'*reduce(operator.mul,dims)*BYTE_SIZES[typeCode] ) )
        @classmethod
        def ones( cls, dims, typeCode=None ):
            """Currently don't allow strings as output types!"""
            raise NotImplementedError( """Have not implemented ones for buffer type""" )
        @classmethod
        def arrayToGLType( cls, value ):
            """Given a value, guess OpenGL type of the corresponding pointer"""
            format = value.format 
            if format in ARRAY_TO_GL_TYPE_MAPPING:
                return ARRAY_TO_GL_TYPE_MAPPING[format]
            raise TypeError( 'Unknown format: %r'%(format,))
        @classmethod
        def arraySize( cls, value, typeCode = None ):
            """Given a data-value, calculate ravelled size for the array"""
            return value.len // value.itemsize
        @classmethod
        def arrayByteCount( cls, value, typeCode = None ):
            """Given a data-value, calculate number of bytes required to represent"""
            return value.len
        @classmethod 
        def unitSize( cls, value, default=None ):
            return value.dims[-1]
        @classmethod
        def asArray( cls, value, typeCode=None ):
            """Convert given value to an array value of given typeCode"""
            buf = _buffers.Py_buffer.from_object( value )
            return buf
        @classmethod
        def dimensions( cls, value, typeCode=None ):
            """Determine dimensions of the passed array value (if possible)"""
            return value.dims

ARRAY_TO_GL_TYPE_MAPPING = _buffers.ARRAY_TO_GL_TYPE_MAPPING
BYTE_SIZES = _buffers.BYTE_SIZES
