﻿using System;
using System.Collections.Generic;

namespace Oni.Totoro
{
    internal static class BodyDatWriter
    {
        public static ImporterDescriptor Write(Body body, ImporterFile importer)
        {
            var trcm = importer.CreateInstance(TemplateTag.TRCM);
            var trga = importer.CreateInstance(TemplateTag.TRGA);
            var trta = importer.CreateInstance(TemplateTag.TRTA);
            var tria = importer.CreateInstance(TemplateTag.TRIA);

            var nodes = body.Nodes;
            int nodeCount = nodes.Count;

            var geometryDescriptors = new ImporterDescriptor[nodeCount];
            var translations = new Vector3[nodeCount];
            var indices = new NodeIndices[nodeCount];

            foreach (var node in nodes)
            {
                int nodeIndex = node.Index;

                geometryDescriptors[nodeIndex] = Motoko.GeometryDatWriter.Write(node.Geometry, importer);
                translations[nodeIndex] = node.Translation;

                int childCount = node.Nodes.Count;

                if (childCount > 0)
                {
                    indices[nodeIndex].FirstChildIndex = (byte)node.Nodes[0].Index;

                    int lastChildIndex = childCount - 1;

                    for (int i = 0; i < childCount; i++)
                    {
                        int childIndex = node.Nodes[i].Index;

                        if (i != lastChildIndex)
                            indices[childIndex].SiblingIndex = (byte)node.Nodes[i + 1].Index;

                        indices[childIndex].ParentIndex = (byte)nodeIndex;
                    }
                }
            }

            WriteTRCM(trcm, trga, trta, tria, nodeCount);
            WriteTRGA(trga, geometryDescriptors);
            WriteTRTA(trta, translations);
            WriteTRIA(tria, indices);

            return trcm;
        }

        private static void WriteTRCM(ImporterDescriptor trcm, ImporterDescriptor trga, ImporterDescriptor trta, ImporterDescriptor tria, int nodeCount)
        {
            using (var writer = trcm.OpenWrite(4))
            {
                writer.WriteInt16(nodeCount);
                writer.Skip(78);
                writer.Write(trga);
                writer.Write(trta);
                writer.Write(tria);
            }
        }

        private static void WriteTRGA(ImporterDescriptor trga, ImporterDescriptor[] descriptors)
        {
            using (var writer = trga.OpenWrite(22))
            {
                writer.WriteInt16(descriptors.Length);
                writer.Write(descriptors);
            }
        }

        private static void WriteTRTA(ImporterDescriptor trta, Vector3[] translations)
        {
            using (var writer = trta.OpenWrite(22))
            {
                writer.WriteInt16(translations.Length);
                writer.Write(translations);
            }
        }

        private struct NodeIndices
        {
            public byte ParentIndex;
            public byte FirstChildIndex;
            public byte SiblingIndex;
        }

        private static void WriteTRIA(ImporterDescriptor tria, NodeIndices[] indices)
        {
            using (var writer = tria.OpenWrite(22))
            {
                writer.WriteInt16(indices.Length);

                foreach (var node in indices)
                {
                    writer.WriteByte(node.ParentIndex);
                    writer.WriteByte(node.FirstChildIndex);
                    writer.WriteByte(node.SiblingIndex);
                    writer.WriteByte(0);
                }
            }
        }
    }
}
