﻿using System;
using System.Collections.Generic;
using System.IO;

namespace Oni.Totoro
{
    internal class BodyDaeReader
    {
        private Body body;
        private float shellOffset = 0.07f;
        private bool generateNormals;
        private bool flatNormals;

        private BodyDaeReader()
        {
        }

        public static Body Read(Dae.Scene scene)
        {
            var reader = new BodyDaeReader
            {
                body = new Body()
            };

            reader.ReadBodyParts(scene);
            return reader.body;
        }

        public static Body Read(Dae.Scene scene, bool generateNormals, bool flatNormals, float shellOffset)
        {
            var reader = new BodyDaeReader
            {
                body = new Body(),
                flatNormals = flatNormals,
                generateNormals = generateNormals,
                shellOffset = shellOffset
            };

            reader.ReadBodyParts(scene);
            return reader.body;
        }

        private void ReadBodyParts(Dae.Scene scene)
        {
            var rootBodyNode = FindRootNode(scene);

            if (rootBodyNode == null)
                throw new InvalidDataException("The scene does not contain any geometry nodes.");

            //
            // Make sure the pelvis is not translated from origin.
            //

            rootBodyNode.Translation = Vector3.Zero;

            if (body.Nodes.Count != 19)
                Console.Error.WriteLine("Non standard bone count: {0}", body.Nodes.Count);
        }

        private BodyNode FindRootNode(Dae.Node daeNode)
        {
            if (daeNode.GeometryInstances.Any())
                return ReadNode(daeNode, null);

            foreach (var childDaeNode in daeNode.Nodes)
            {
                var bodyNode = FindRootNode(childDaeNode);

                if (bodyNode != null)
                    return bodyNode;
            }

            return null;
        }

        private BodyNode ReadNode(Dae.Node daeNode, BodyNode parentNode)
        {
            var bodyNode = new BodyNode
            {
                DaeNode = daeNode,
                Parent = parentNode,
                Index = body.Nodes.Count
            };

            body.Nodes.Add(bodyNode);

            //
            // Find and read the geometry for this node
            //

            foreach (var geometryInstance in daeNode.GeometryInstances.Where(n => n.Target != null))
            {
                var daeGeometry = geometryInstance.Target;

                if (bodyNode.Geometry != null)
                    Console.Error.WriteLine("The node {0} contains more than one geometry. Only the first geometry will be used.", daeGeometry.Name);

                bodyNode.Geometry = Motoko.GeometryDaeReader.Read(daeGeometry, generateNormals, flatNormals, shellOffset);
            }

            //
            // Extract the translation part of this node's transform
            //

            bodyNode.Translation = daeNode.Transforms.ToMatrix().Translation;

            //
            // Read child nodes
            //

            foreach (var daeChildNode in daeNode.Nodes)
                bodyNode.Nodes.Add(ReadNode(daeChildNode, parentNode));

            return bodyNode;
        }
    }
}
