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))
