using System;
using System.IO;
using System.Runtime.InteropServices;

namespace MoonWorks.Audio
{
    public class StaticSound : IDisposable
    {
        internal AudioDevice Device { get; }
        internal FAudio.FAudioBuffer Handle;
        public ushort Channels { get; }
        public uint SamplesPerSecond { get; }

        public uint LoopStart { get; set; } = 0;
        public uint LoopLength { get; set; } = 0;

        private bool IsDisposed;

        public static StaticSound LoadOgg(AudioDevice device, FileInfo fileInfo)
        {
            var filePointer = FAudio.stb_vorbis_open_filename(fileInfo.FullName, out var error, IntPtr.Zero);

            if (error != 0)
            {
                throw new AudioLoadException("Error loading file!");
            }
            var info = FAudio.stb_vorbis_get_info(filePointer);
            var bufferSize = FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
            var buffer = new float[bufferSize];

            FAudio.stb_vorbis_get_samples_float_interleaved(
                filePointer,
                info.channels,
                buffer,
                (int) bufferSize
            );

            FAudio.stb_vorbis_close(filePointer);

            return new StaticSound(
                device,
                (ushort) info.channels,
                info.sample_rate,
                buffer,
                0,
                (uint) buffer.Length
            );
        }

        public StaticSound(
            AudioDevice device,
            ushort channels,
            uint samplesPerSecond,
            float[] buffer,
            uint bufferOffset, /* in floats */
            uint bufferLength  /* in floats */
        ) {
            Device = device;

            Channels = channels;
            SamplesPerSecond = samplesPerSecond;

            var bufferLengthInBytes = (int) (bufferLength * sizeof(float));
            Handle = new FAudio.FAudioBuffer();
            Handle.Flags = FAudio.FAUDIO_END_OF_STREAM;
            Handle.pContext = IntPtr.Zero;
            Handle.AudioBytes = (uint) bufferLengthInBytes;
            Handle.pAudioData = Marshal.AllocHGlobal(bufferLengthInBytes);
            Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength);
            Handle.PlayBegin = 0;
            Handle.PlayLength = 0;

            LoopStart = 0;
            LoopLength = 0;
        }

        public StaticSoundInstance CreateInstance(bool loop = false)
        {
            return new StaticSoundInstance(Device, this, false, loop);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!IsDisposed)
            {
                if (disposing)
                {
                    // dispose managed state (managed objects)
                }

                Marshal.FreeHGlobal(Handle.pAudioData);
                IsDisposed = true;
            }
        }

        // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
        ~StaticSound()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: false);
        }

        public void Dispose()
        {
            // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
            Dispose(disposing: true);
            GC.SuppressFinalize(this);
        }
    }
}