﻿using System;
using System.IO;

namespace Oni.Sound
{
    internal class WavImporter : Importer
    {
        private readonly bool convertToDemo;

        public WavImporter(bool toDemo)
            : base(toDemo?InstanceFileHeader.OniMacTemplateChecksum:InstanceFileHeader.OniPCTemplateChecksum)
        {
            convertToDemo = toDemo;
        }

        public override void Import(string filePath, string outputDirPath)
        {
            var wav = WavFile.FromFile(filePath);

            if (wav.Format != WavFormat.Pcm && wav.Format != WavFormat.Adpcm)
            {
                Console.Error.WriteLine("Unsupported WAV format (0x{0:X})", wav.Format);
                return;
            }

            if (wav.ChannelCount != 1 && wav.ChannelCount != 2)
            {
                Console.Error.WriteLine("Unsupported number of channels ({0})", wav.ChannelCount);
                return;
            }

            if (wav.SampleRate != 22050 && wav.SampleRate != 44100)
            {
                Console.Error.WriteLine("Unsupported sample rate ({0} Hz)", wav.SampleRate);
                return;
            }

            if (wav.ExtraData.Length > 32)
                throw new NotSupportedException(string.Format("Unsupported wave format extra data size ({0})", wav.ExtraData.Length));

            BeginImport();
            WriteSNDD(Path.GetFileNameWithoutExtension(filePath), wav);
            Write(outputDirPath);
        }

        private void WriteSNDD(string name, WavFile wav)
        {
            float duration = wav.SampleCount * 60.0f / wav.SampleRate;

            var sndd = CreateInstance(TemplateTag.SNDD, name);
            if (convertToDemo)
            {
                // TODO: also validate other ADPCM parameters (coefficient table)?
                if (wav.Format != WavFormat.Adpcm) // Or is PCM supported, actually?
                    Console.WriteLine("Cannot convert to PC demo: ADPCM format required!");
                if (wav.SampleRate != 22050)
                    Console.WriteLine("Cannot convert to PC demo: 22050 kHz required!");
                if (wav.BlockAlign != 512 * wav.ChannelCount)
                    Console.WriteLine("Cannot convert to PC demo: wrong block alignment!");
                if (wav.BitsPerSample != 4)
                    Console.WriteLine("Cannot convert to PC demo: wrong bits per sample!");

                using (var writer = sndd.OpenWrite())
                {
                    if (wav.ChannelCount == 2)
                        writer.WriteInt16(3);
                    else 
                        writer.WriteInt16(1);
                    writer.WriteInt16(0);
                    writer.Write((int)duration);
                    writer.Write(wav.SoundData.Length);
                    writer.Write(WriteRawPart(wav.SoundData));
                    writer.Skip(8);
                }
            }
            else
            {
                using (var writer = sndd.OpenWrite())
                {
                    if (wav.Format == WavFormat.Adpcm)
                        writer.WriteInt16(8);
                    else
                        writer.WriteInt16(0);
                    writer.WriteInt16(0);
                    writer.Write((short)wav.Format);
                    writer.WriteInt16(wav.ChannelCount);
                    writer.Write(wav.SampleRate);
                    writer.Write(wav.AverageBytesPerSecond);
                    writer.WriteInt16(wav.BlockAlign);
                    writer.WriteInt16(wav.BitsPerSample);
                    writer.WriteInt16(wav.ExtraData.Length);
                    writer.Write(wav.ExtraData);
                    writer.Skip(32 - wav.ExtraData.Length);
                    writer.Write((short)duration);
                    writer.Write(wav.SoundData.Length);
                    writer.Write(WriteRawPart(wav.SoundData));
                    writer.Skip(24);
                }
            }
        }
    }
}
