using System;
using System.IO;
using ANX.Framework.Audio;
using ANX.Framework.NonXNA.Development;
using ANX.Framework.NonXNA.SoundSystem;
using OpenTK.Audio.OpenAL;
using WaveUtils;
using ALFormat = OpenTK.Audio.OpenAL.ALFormat;

// This file is part of the ANX.Framework created by the
// "ANX.Framework developer group" and released under the Ms-PL license.
// For details see: http://anxframework.codeplex.com/license

namespace ANX.SoundSystem.OpenAL
{
    [Developer("AstrorEnales")]
	public class OpenALSoundEffect : ISoundEffect
	{
		#region Private
		private WaveInfo waveInfo;
		private TimeSpan duration;
        internal int BufferHandle { get; private set; }
		#endregion

		#region Public
	    public TimeSpan Duration
	    {
	        get { return duration; }
	    }
	    #endregion

		#region Constructor
		internal OpenALSoundEffect(Stream stream)
		{
			CreateFromStream(stream);
		}

		internal OpenALSoundEffect(byte[] buffer, int offset, int count, int sampleRate, AudioChannels channels,
            int loopStart, int loopLength)
		{
            // TODO: loopStart and loopLength

            byte[] subBuffer = new byte[count];
            Array.Copy(buffer, offset, subBuffer, 0, count);
            BufferHandle = AL.GenBuffer();

            // TODO: evaluate if 8bit or 16bit!!
		    ALFormat format = channels == AudioChannels.Mono ? ALFormat.Mono8 : ALFormat.Stereo8;
            AL.BufferData(BufferHandle, format, subBuffer, count, sampleRate);

            float sizeMulBlockAlign = count / ((int)channels * 2f);
            duration = TimeSpan.FromMilliseconds(sizeMulBlockAlign * 1000f / sampleRate);

            ALError error = AL.GetError();
            if (error != ALError.NoError)
                throw new Exception("OpenAL error " + error + ": " + AL.GetErrorString(error));
		}
		#endregion

		#region CreateFromStream
		private void CreateFromStream(Stream stream)
		{
			waveInfo = WaveFile.LoadData(stream);
			if (waveInfo.WaveFormat != WaveFormat.PCM)
			{
				var converter = new WaveConverter(waveInfo);
				converter.ConvertToPcm();
			}

			duration = waveInfo.CalculateDuration();

			BufferHandle = AL.GenBuffer();
			AL.BufferData(BufferHandle, (ALFormat)waveInfo.ALFormat, waveInfo.Data, waveInfo.Data.Length, waveInfo.SampleRate);

			ALError error = AL.GetError();
			if (error != ALError.NoError)
				throw new Exception("OpenAL error " + error + ": " + AL.GetErrorString(error));
		}
		#endregion

		#region Dispose
		public void Dispose()
		{
			waveInfo = null;
			AL.DeleteBuffer(BufferHandle);
		}
		#endregion
	}
}