﻿using System;
using System.Collections.Generic;

namespace Oni.Motoko
{
    internal static class GeometryDatReader
    {
        public static Geometry Read(InstanceDescriptor m3gm)
        {
            if (m3gm.Template.Tag != TemplateTag.M3GM)
                throw new ArgumentException(string.Format("Invalid instance type {0}", m3gm.Template.Tag), "m3gm");

            InstanceDescriptor pnta;
            InstanceDescriptor vcra1;
            InstanceDescriptor vcra2;
            InstanceDescriptor txca;
            InstanceDescriptor idxa1;
            InstanceDescriptor idxa2;
            InstanceDescriptor txmp;

            using (var reader = m3gm.OpenRead(4))
            {
                pnta = reader.ReadInstance();
                vcra1 = reader.ReadInstance();
                vcra2 = reader.ReadInstance();
                txca = reader.ReadInstance();
                idxa1 = reader.ReadInstance();
                idxa2 = reader.ReadInstance();
                txmp = reader.ReadInstance();
            }

            var geometry = new Geometry {
                Name = m3gm.FullName,
                Texture = txmp
            };

            Vector3[] faceNormals;
            int[] faceIndices;
            int[] vertexIndices;

            using (var reader = pnta.OpenRead(52))
                geometry.Points = reader.ReadVector3Array(reader.ReadInt32());

            using (var reader = vcra1.OpenRead(20))
                geometry.Normals = reader.ReadVector3Array(reader.ReadInt32());

            using (var reader = vcra2.OpenRead(20))
                faceNormals = reader.ReadVector3Array(reader.ReadInt32());

            using (var reader = txca.OpenRead(20))
                geometry.TexCoords = reader.ReadVector2Array(reader.ReadInt32());

            using (var reader = idxa1.OpenRead(20))
                vertexIndices = reader.ReadInt32Array(reader.ReadInt32());

            using (var reader = idxa2.OpenRead(20))
                faceIndices = reader.ReadInt32Array(reader.ReadInt32());

            geometry.Triangles = ConvertTriangleStripToTriangleList(geometry.Points, vertexIndices, faceNormals, faceIndices);

            return geometry;
        }

        private static int[] ConvertTriangleStripToTriangleList(Vector3[] points, int[] vIndices, Vector3[] fNormals, int[] fIndices)
        {
            var triangles = new List<int>(vIndices.Length * 2);

            var face = new int[3];
            int faceIndex = 0;
            int order = 0;

            for (int i = 0; i < vIndices.Length; i++)
            {
                if (vIndices[i] < 0)
                {
                    face[0] = vIndices[i++] & int.MaxValue;
                    face[1] = vIndices[i++];
                    order = 0;
                }
                else
                {
                    face[order] = face[2];
                    order ^= 1;
                }

                face[2] = vIndices[i];

                var v1 = points[face[0]];
                var v2 = points[face[1]];
                var v3 = points[face[2]];

                var faceNormal1 = Vector3.Normalize(fNormals[fIndices[faceIndex]]);
                var faceNormal2 = Vector3.Normalize(Vector3.Cross(v2 - v1, v3 - v1));

                if (Vector3.Dot(faceNormal1, faceNormal2) < 0.0f)
                {
                    triangles.Add(face[2]);
                    triangles.Add(face[1]);
                    triangles.Add(face[0]);
                }
                else
                {
                    triangles.Add(face[0]);
                    triangles.Add(face[1]);
                    triangles.Add(face[2]);
                }

                faceIndex++;
            }

            return triangles.ToArray();
        }
    }
}
