from __future__ import print_function
from __future__ import division
from __future__ import absolute_import

from compas.files.gltf.data_classes import AnimationData
from compas.files.gltf.data_classes import AnimationSamplerData
from compas.files.gltf.data_classes import CameraData
from compas.files.gltf.data_classes import ChannelData
from compas.files.gltf.data_classes import MaterialData
from compas.files.gltf.data_classes import PrimitiveData
from compas.files.gltf.data_classes import SamplerData
from compas.files.gltf.data_classes import SkinData
from compas.files.gltf.data_classes import TextureData
from compas.files.gltf.gltf_content import GLTFContent
from compas.files.gltf.gltf_mesh import GLTFMesh
from compas.files.gltf.gltf_node import GLTFNode
from compas.files.gltf.gltf_scene import GLTFScene


class GLTFParser(object):
    """Parse the contents of the reader into a GLTFContent object.

    Parameters
    ----------
    reader : :class:`compas.files.GLTFReader`

    Attributes
    ----------
    reader : :class:`compas.files.GLTFReader`
    content : :class:`compas.files.GLTFContent`
    """

    def __init__(self, reader):
        self.reader = reader
        self.content = GLTFContent()

        self.parse()

    def parse(self):
        self.content.default_scene_key = self._get_default_scene()
        self.content.extras = self._get_extras()
        self.content.extensions = self._get_extensions()

        self.content.images = {key: image_data for key, image_data in enumerate(self.reader.image_data)}
        self.content.samplers = {key: SamplerData.from_data(sampler) for key, sampler in enumerate(self.reader.json.get('samplers', []))}
        self.content.textures = {key: TextureData.from_data(texture) for key, texture in enumerate(self.reader.json.get('textures', []))}
        self.content.materials = {key: MaterialData.from_data(material) for key, material in enumerate(self.reader.json.get('materials', []))}
        self.content.cameras = {key: CameraData.from_data(camera) for key, camera in enumerate(self.reader.json.get('cameras', []))}
        self.content.skins = {key: SkinData.from_data(skin, self.reader.data[skin['inverseBindMatrices']]) for key, skin in enumerate(self.reader.json.get('skins', []))}
        self.content.animations = {key: self._get_animation_data(animation) for key, animation in enumerate(self.reader.json.get('animations', []))}

        for mesh in self.reader.json.get('meshes', []):
            self._add_gltf_mesh(mesh)
        for node in self.reader.json.get('nodes', []):
            self._add_gltf_node(node)
        for scene in self.reader.json.get('scenes', []):
            self._add_gltf_scene(scene)

        self.content.update_node_transforms_and_positions()

    def _get_animation_data(self, animation):
        sampler_data_dict = {}
        for index, sampler in enumerate(animation['samplers']):
            input_ = self.reader.data[sampler['input']]
            output = self.reader.data[sampler['output']]
            sampler_data_dict[index] = AnimationSamplerData.from_data(sampler, input_, output)
        channel_data_list = [ChannelData.from_data(channel) for channel in animation['channels']]
        return AnimationData.from_data(animation, channel_data_list, sampler_data_dict)

    def _get_extras(self):
        return self.reader.json.get('extras')

    def _get_default_scene(self):
        return self.reader.json.get('scene')

    def _get_extensions(self):
        return self.reader.json.get('extensions')

    def _add_gltf_scene(self, scene):
        GLTFScene.from_data(scene, self.content)

    def _add_gltf_node(self, node):
        GLTFNode.from_data(node, self.content)

    def _add_gltf_mesh(self, mesh):
        primitive_data_list = []
        for primitive in mesh['primitives']:
            if 'POSITION' not in primitive['attributes']:
                continue

            attributes = {}
            for attr, attr_accessor_index in primitive['attributes'].items():
                attributes[attr] = self.reader.data[attr_accessor_index]

            indices = self._get_indices(primitive, len(attributes['POSITION']))

            target_list = []
            for target in primitive.get('targets', []):
                target_data = {attr: self.reader.data[accessor_index] for attr, accessor_index in target.items()}
                target_list.append(target_data)

            primitive_data = PrimitiveData.from_data(primitive, attributes, indices, target_list)

            primitive_data_list.append(primitive_data)

        GLTFMesh.from_data(mesh, self.content, primitive_data_list)

    def _get_indices(self, primitive, num_vertices):
        if 'indices' not in primitive:
            return self.get_generic_indices(num_vertices)
        indices_accessor_index = primitive['indices']
        return self.reader.data[indices_accessor_index]

    def get_generic_indices(self, num_vertices):
        return list(range(num_vertices))
