﻿using System;
using System.Xml;
using Oni.Metadata;
using Oni.Xml;

namespace Oni.Sound
{
    internal sealed class OsbdXmlExporter : RawXmlExporter
    {
        private OsbdXmlExporter(BinaryReader reader, XmlWriter xml)
            : base(reader, xml)
        {
        }

        public static void Export(BinaryReader reader, XmlWriter xml)
        {
            var exporter = new OsbdXmlExporter(reader, xml);
            exporter.Export();
        }

        private void Export()
        {
            int tag = Reader.ReadInt32();
            int size = Reader.ReadInt32();
            int version = Reader.ReadInt32();

            if (version > 6)
                throw new NotSupportedException("Sound version {0} is not supported");

            switch (tag)
            {
                case SoundMetadata.OSAm:
                    Xml.WriteStartElement("AmbientSound");
                    ExportAmbient(version);
                    break;

                case SoundMetadata.OSGr:
                    Xml.WriteStartElement("SoundGroup");
                    ExportGroup(version);
                    break;

                case SoundMetadata.OSIm:
                    Xml.WriteStartElement("ImpulseSound");
                    ExportImpulse(version);
                    break;

                default:
                    throw new NotSupportedException(string.Format("Unknown sound binary data tag {0:X}", tag));
            }

            Xml.WriteEndElement();
        }

        private void ExportGroup(int version)
        {
            if (version < 6)
            {
                float volume = 1.0f;
                float pitch = 1.0f;
                var flags = SoundMetadata.OSGrFlags.None;
                int channelCount;
                int permutationCount;

                if (version >= 2)
                    volume = Reader.ReadSingle();

                if (version >= 3)
                    pitch = Reader.ReadSingle();

                channelCount = Reader.ReadInt32();
                permutationCount = Reader.ReadInt32();

                if (permutationCount >= 4)
                    flags |= SoundMetadata.OSGrFlags.PreventRepeat;

                Xml.WriteElementString("Volume", XmlConvert.ToString(volume));
                Xml.WriteElementString("Pitch", XmlConvert.ToString(pitch));
                Xml.WriteStartElement("Flags");
                Xml.WriteString(MetaEnum.ToString(flags));
                Xml.WriteEndElement();
                Xml.WriteElementString("NumberOfChannels", XmlConvert.ToString(channelCount));
                Xml.WriteStartElement("Permutations");

                for (int i = 0; i < permutationCount; i++)
                {
                    Xml.WriteStartElement("Permutation");
                    SoundMetadata.osgrPermutation.Accept(this);
                    Xml.WriteEndElement();
                }

                Xml.WriteEndElement();
            }
            else
            {
                SoundMetadata.osgr6.Accept(this);
            }
        }

        private void ExportAmbient(int version)
        {
            if (version <= 4)
            {
                SoundMetadata.osam4.Accept(this);
                Xml.WriteElementString("Treshold", "3");
                Xml.WriteElementString("MinOcclusion", "0");
            }
            else if (version <= 5)
            {
                SoundMetadata.osam5.Accept(this);
                Xml.WriteElementString("MinOcclusion", "0");
            }
            else
            {
                SoundMetadata.osam6.Accept(this);
            }
        }

        private void ExportImpulse(int version)
        {
            if (version <= 3)
            {
                SoundMetadata.osim3.Accept(this);
                Xml.WriteStartElement("AlternateImpulse");
                Xml.WriteElementString("Treshold", "0");
                Xml.WriteStartElement("Impulse");
                Xml.WriteString("");
                Xml.WriteEndElement();
                Xml.WriteEndElement();
                Xml.WriteElementString("ImpactVelocity", "0");
                Xml.WriteElementString("MinOcclusion", "0");
            }
            else if (version <= 4)
            {
                SoundMetadata.osim4.Accept(this);
                Xml.WriteElementString("ImpactVelocity", "0");
                Xml.WriteElementString("MinOcclusion", "0");
            }
            else if (version <= 5)
            {
                SoundMetadata.osim5.Accept(this);
                Xml.WriteElementString("MinOcclusion", "0");
            }
            else
            {
                SoundMetadata.osim6.Accept(this);
            }
        }
    }
}
