﻿using System;
using System.Collections.Generic;
using Oni.Collections;

namespace Oni.Dae
{
    internal class UnitConverter
    {
        private Scene scene;
        private float scale;
        private Set<float[]> scaledValues;

        public static void Convert(Scene scene, float scale)
        {
            var converter = new UnitConverter {
                scene = scene,
                scale = scale,
                scaledValues = new Set<float[]>()
            };

            converter.Convert();
        }

        private void Convert()
        {
            Convert(scene);
        }

        private void Convert(Node node)
        {
            foreach (var transform in node.Transforms)
                Convert(transform);

            foreach (var instance in node.Instances)
                Convert(instance);

            foreach (var child in node.Nodes)
                Convert(child);
        }

        private void Convert(Instance instance)
        {
            var geometryInstance = instance as GeometryInstance;

            if (geometryInstance != null)
            {
                Convert(geometryInstance.Target);
                return;
            }
        }

        private void Convert(Geometry geometry)
        {
            foreach (var primitives in geometry.Primitives)
            {
                //
                // TODO: this assumes that position sources are not reused.
                //

                foreach (var input in primitives.Inputs)
                {
                    if (input.Semantic == Semantic.Position)
                        Scale(input.Source.FloatData, input.Source.Stride);
                }
            }
        }

        private void Convert(Transform transform)
        {
            var translate = transform as TransformTranslate;

            if (translate != null)
            {
                Scale(translate.Values, 3);

                if (translate.HasAnimations)
                {
                    for (int i = 0; i < translate.Animations.Length; i++)
                    {
                        Sampler s = translate.Animations[i];
                        translate.Animations[i] = s == null ? null : s.Scale(scale);
                    }
                }

                return;
            }

            var matrix = transform as TransformMatrix;

            if (matrix != null)
            {
                matrix.Values[3] *= scale;
                matrix.Values[7] *= scale;
                matrix.Values[11] *= scale;

                return;
            }
        }

        private void Scale(float[] values, int stride)
        {
            if (!scaledValues.Add(values))
                return;

            for (int i = 0; i + stride - 1 < values.Length; i += stride)
            {
                values[i + 0] *= scale;
                values[i + 1] *= scale;
                values[i + 2] *= scale;
            }
        }
    }
}
