#! /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 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.warning(
                "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[0] >= 3:

            @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""" )
                # TODO: only do this IFF value.internal is None
                return _types.GLvoidp(value.buf)

        #                return value
        else:

            @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
