# Modified from the original OpenCTM python binding
# for inclusion in the `trimesh` package:
# https://github.com/mikedh/trimesh
#
# To get shared library this binding imports, you can download
# and install it on Linux using this bash script:
#  https://github.com/mikedh/trimesh/blob/master/docker/builds/openctm.bash
# ------------------------------------------------------------------------------
# Copyright (c) 2009-2010 Marcus Geelnard
#
# This software is provided 'as-is', without any express or implied
# warranty. In no event will the authors be held liable for any damages
# arising from the use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
#     1. The origin of this software must not be misrepresented; you must not
#     claim that you wrote the original software. If you use this software
#     in a product, an acknowledgment in the product documentation would be
#     appreciated but is not required.
#
#     2. Altered source versions must be plainly marked as such, and must not
#     be misrepresented as being the original software.
#
#     3. This notice may not be removed or altered from any source
#     distribution.
# ------------------------------------------------------------------------------

import os
import ctypes

_ctm_loaders = {}
_ctm_lib = None
try:
    if os.name == 'nt':
        # try to import the shared library on windows
        _ctm_lib = ctypes.WinDLL('openctm.dll')
    else:
        # try to import on other platforms
        _ctm_lib_name = ctypes.util.find_library('openctm')
        if _ctm_lib_name:
            _ctm_lib = ctypes.CDLL(_ctm_lib_name)
except BaseException:
    pass

if _ctm_lib:
    import numpy as np

    # Types
    CTMfloat = ctypes.c_float
    CTMint = ctypes.c_int32
    CTMuint = ctypes.c_uint32
    CTMcontext = ctypes.c_void_p
    CTMenum = ctypes.c_uint32

    # boolean
    CTM_TRUE = 1
    CTM_FALSE = 0

    # CTMenum
    CTM_NONE = 0x0000
    CTM_IMPORT = 0x0101
    CTM_EXPORT = 0x0102
    CTM_VERTEX_COUNT = 0x0301
    CTM_TRIANGLE_COUNT = 0x0302
    CTM_HAS_NORMALS = 0x0303
    CTM_INDICES = 0x0601
    CTM_VERTICES = 0x0602
    CTM_NORMALS = 0x0603

    # Functions
    ctmNewContext = _ctm_lib.ctmNewContext
    ctmNewContext.argtypes = [CTMenum]
    ctmNewContext.restype = CTMcontext
    ctmFreeContext = _ctm_lib.ctmFreeContext
    ctmFreeContext.argtypes = [CTMcontext]
    ctmGetError = _ctm_lib.ctmGetError
    ctmGetError.argtypes = [CTMcontext]
    ctmGetError.restype = CTMenum
    ctmErrorString = _ctm_lib.ctmErrorString
    ctmErrorString.argtypes = [CTMenum]
    ctmErrorString.restype = ctypes.c_char_p
    ctmGetInteger = _ctm_lib.ctmGetInteger
    ctmGetInteger.argtypes = [CTMcontext, CTMenum]
    ctmGetInteger.restype = CTMint
    ctmGetFloat = _ctm_lib.ctmGetFloat
    ctmGetFloat.argtypes = [CTMcontext, CTMenum]
    ctmGetFloat.restype = CTMfloat
    ctmGetIntegerArray = _ctm_lib.ctmGetIntegerArray
    ctmGetIntegerArray.argtypes = [CTMcontext, CTMenum]
    ctmGetIntegerArray.restype = ctypes.POINTER(CTMuint)
    ctmGetFloatArray = _ctm_lib.ctmGetFloatArray
    ctmGetFloatArray.argtypes = [CTMcontext, CTMenum]
    ctmGetFloatArray.restype = ctypes.POINTER(CTMfloat)
    ctmLoad = _ctm_lib.ctmLoad
    ctmLoad.argtypes = [CTMcontext, ctypes.c_char_p]
    ctmSave = _ctm_lib.ctmSave
    ctmSave.argtypes = [CTMcontext, ctypes.c_char_p]

    def load_ctm(file_obj, file_type=None):
        """
        Load OpenCTM files from a file object.

        Parameters
        ----------
        file_obj : open file- like object

        Returns
        ----------
        loaded : dict
                  kwargs for a Trimesh constructor:
                    {vertices: (n,3) float, vertices
                     faces:    (m,3) int, indexes of vertices}
        """
        ctm = ctmNewContext(CTM_IMPORT)

        # !!load file from name
        # this should be replaced with something that
        # actually uses the file object data to support streams
        name = str(file_obj.name).encode('utf-8')
        ctmLoad(ctm, name)

        err = ctmGetError(ctm)
        if err != CTM_NONE:
            raise IOError("Error loading file: " + str(ctmErrorString(err)))

        # get vertices
        vertex_count = ctmGetInteger(ctm, CTM_VERTEX_COUNT)
        vertex_ctm = ctmGetFloatArray(ctm, CTM_VERTICES)
        # use fromiter to avoid loop
        vertices = np.fromiter(vertex_ctm,
                               dtype=np.float,
                               count=vertex_count * 3).reshape((-1, 3))
        # get faces
        face_count = ctmGetInteger(ctm, CTM_TRIANGLE_COUNT)
        face_ctm = ctmGetIntegerArray(ctm, CTM_INDICES)
        faces = np.fromiter(face_ctm,
                            dtype=np.int,
                            count=face_count * 3).reshape((-1, 3))

        # create kwargs for trimesh constructor
        result = {'vertices': vertices,
                  'faces': faces}

        # get face normals if available
        if ctmGetInteger(ctm, CTM_HAS_NORMALS) == CTM_TRUE:
            normals_ctm = ctmGetFloatArray(ctm, CTM_NORMALS)
            normals = np.fromiter(normals_ctm,
                                  dtype=np.float,
                                  count=face_count * 3).reshape((-1, 3))
            result['face_normals'] = normals

        # free context
        ctmFreeContext(ctm)

        return result

    # we have a library so add load_ctm
    _ctm_loaders = {'ctm': load_ctm}
