﻿using System;
using System.Collections.Generic;
using System.Xml;
using Oni.Metadata;
using Oni.Particles;
using Oni.Xml;

namespace Oni.Sound
{
    internal class SabdXmlImporter : RawXmlImporter
    {
        #region Private data
        private static readonly Dictionary<string, string> modifierMap = new Dictionary<string, string>();
        private static readonly Dictionary<string, string> typeMap = new Dictionary<string, string>();
        #endregion

        static SabdXmlImporter()
        {
            var modifiers = new[] {
                "Any", "Crouch", "Jump", "Heavy Damage", "Medium Damage", "Light Damage"
            };

            var types = new[] {
                "Block", "Draw Weapon", "Fall", "Fly", "Getting Hit", "Holster",
                "Kick", "Knockdown", "Land", "Jump", "Pickup", "Punch",
                "Reload Pistol", "Reload Rifle", "Reload Stream", "Reload Superball",
                "Reload Vandegraf", "Reload Scram Cannon", "Reload Mercury Bow", "Reload Screamer",
                "Run", "Slide", "Stand", "Starle", "Walk", "Powerup", "Roll", "Falling Flail"
            };

            foreach (string modifier in modifiers)
                modifierMap.Add(modifier.Replace(" ", ""), modifier);

            foreach (string type in types)
                typeMap.Add(type.Replace(" ", ""), type);
        }

        private SabdXmlImporter(XmlReader reader, BinaryWriter writer)
            : base(reader, writer)
        {
        }

        public static void Import(XmlReader reader, BinaryWriter writer)
        {
            var importer = new SabdXmlImporter(reader, writer);
            importer.Import();
        }

        #region private class SoundAnimationData

        private class SoundAnimationData
        {
            #region Private data
            private string variant;
            private List<SoundAssignment> assignments;
            #endregion

            private enum Tag
            {
                SAFT = 0x54464153,
                SAVT = 0x54564153,
                SASA = 0x41534153
            }

            public SoundAnimationData(XmlReader xml)
            {
                variant = xml.GetAttribute("Variant");

                xml.ReadStartElement("SoundAnimation");
                assignments = new List<SoundAssignment>();

                while (xml.IsStartElement("Assignment"))
                {
                    xml.ReadStartElement();
                    assignments.Add(new SoundAssignment(xml));
                    xml.ReadEndElement();
                }

                xml.ReadEndElement();
            }

            public void Write(BinaryWriter writer)
            {
                writer.Write((int)Tag.SAFT);
                writer.Write(4);
                writer.Write(6);
                writer.Write((int)Tag.SAVT);
                writer.Write(32);
                writer.Write(variant, 32);

                foreach (SoundAssignment assignment in assignments)
                {
                    writer.Write((int)Tag.SASA);
                    writer.Write(132);
                    assignment.Write(writer);
                }
            }
        }

        #endregion
        #region private class SoundAssignment

        private class SoundAssignment
        {
            #region Private data
            private int frame;
            private string modifier;
            private string type;
            private string animationName;
            private string soundName;
            #endregion

            public SoundAssignment(XmlReader xml)
            {
                xml.ReadStartElement("Target");

                if (xml.LocalName == "Animation")
                {
                    type = "Animation";
                    animationName = xml.ReadElementContentAsString("Animation", "");
                }
                else
                {
                    type = xml.ReadElementContentAsString("Type", "");
                    animationName = string.Empty;

                    if (!typeMap.TryGetValue(type, out type))
                        throw new NotSupportedException(string.Format("Unknown assignment type '{0}' found", type));
                }

                if (xml.IsStartElement("Modifier"))
                    modifier = xml.ReadElementContentAsString();
                else
                    modifier = "Any";

                if (!modifierMap.TryGetValue(modifier, out modifier))
                    throw new NotSupportedException(string.Format("Unknown assignment modifier '{0}' found", modifier));

                xml.ReadStartElement("Frame");
                frame = xml.ReadContentAsInt();
                xml.ReadEndElement();

                xml.ReadEndElement();

                soundName = xml.ReadElementContentAsString("Sound", "");
            }

            public void Write(BinaryWriter writer)
            {
                writer.Write(frame);
                writer.Write(modifier, 32);
                writer.Write(type, 32);
                writer.Write(animationName, 32);
                writer.Write(soundName, 32);
            }
        }

        #endregion

        private void Import()
        {
            var data = new SoundAnimationData(Xml);
            data.Write(Writer);
        }
    }
}
