using System;
using System.Collections.Generic;
using OpenTK.Audio;
using OpenTK.Audio.OpenAL;

namespace SharpQuake
{
	internal class OpenALController : ISoundController
	{
		private void FreeContext()
		{
			if (this._Source != 0)
			{
				AL.SourceStop(this._Source);
				AL.DeleteSource(this._Source);
				this._Source = 0;
			}
			if (this._Buffers != null)
			{
				AL.DeleteBuffers(this._Buffers);
				this._Buffers = null;
			}
			if (this._Context != null)
			{
				this._Context.Dispose();
				this._Context = null;
			}
		}

		public bool IsInitialized
		{
			get
			{
				return this._IsInitialized;
			}
		}

		public void Init()
		{
			this.FreeContext();
			this._Context = new AudioContext();
			this._Source = AL.GenSource();
			this._Buffers = new int[24];
			this._BufferBytes = new int[24];
			this._FreeBuffers = new Queue<int>(24);
			for (int i = 0; i < this._Buffers.Length; i++)
			{
				this._Buffers[i] = AL.GenBuffer();
				this._FreeBuffers.Enqueue(this._Buffers[i]);
			}
			AL.SourcePlay(this._Source);
			AL.Source(this._Source, ALSourceb.Looping, false);
			snd.shm.channels = 2;
			snd.shm.samplebits = 16;
			snd.shm.speed = 11025;
			snd.shm.buffer = new byte[65536];
			snd.shm.soundalive = true;
			snd.shm.splitbuffer = false;
			snd.shm.samples = snd.shm.buffer.Length / (snd.shm.samplebits / 8);
			snd.shm.samplepos = 0;
			snd.shm.submission_chunk = 1;
			if (snd.shm.samplebits == 8)
			{
				if (snd.shm.channels == 2)
				{
					this._BufferFormat = ALFormat.Stereo8;
				}
				else
				{
					this._BufferFormat = ALFormat.Mono8;
				}
			}
			else if (snd.shm.channels == 2)
			{
				this._BufferFormat = ALFormat.Stereo16;
			}
			else
			{
				this._BufferFormat = ALFormat.Mono16;
			}
			this._IsInitialized = true;
		}

		public void Shutdown()
		{
			this.FreeContext();
			this._IsInitialized = false;
		}

		public void ClearBuffer()
		{
			AL.SourceStop(this._Source);
		}

		public byte[] LockBuffer()
		{
			return snd.shm.buffer;
		}

		public void UnlockBuffer(int bytes)
		{
			int num;
			AL.GetSource(this._Source, ALGetSourcei.BuffersProcessed, out num);
			if (num > 0)
			{
				foreach (int num2 in AL.SourceUnqueueBuffers(this._Source, num))
				{
					if (num2 != 0)
					{
						int num3 = Array.IndexOf<int>(this._Buffers, num2);
						if (num3 != -1)
						{
							this._SamplesSent += this._BufferBytes[num3] >> snd.shm.samplebits / 8 - 1;
							this._SamplesSent &= snd.shm.samples - 1;
							this._BufferBytes[num3] = 0;
						}
						if (!this._FreeBuffers.Contains(num2))
						{
							this._FreeBuffers.Enqueue(num2);
						}
					}
				}
			}
			if (this._FreeBuffers.Count == 0)
			{
				Con.DPrint("UnlockBuffer: No free buffers!\n", Array.Empty<object>());
				return;
			}
			int num4 = this._FreeBuffers.Dequeue();
			if (num4 != 0)
			{
				AL.BufferData<byte>(num4, this._BufferFormat, snd.shm.buffer, bytes, snd.shm.speed);
				AL.SourceQueueBuffer(this._Source, num4);
				int num5 = Array.IndexOf<int>(this._Buffers, num4);
				if (num5 != -1)
				{
					this._BufferBytes[num5] = bytes;
				}
				int num6;
				AL.GetSource(this._Source, ALGetSourcei.SourceState, out num6);
				if (num6 != 4114)
				{
					AL.SourcePlay(this._Source);
					Con.DPrint("Sound resumed from {0}, free {1} of {2} buffers\n", new object[]
					{
						((ALSourceState)num6).ToString("F"),
						this._FreeBuffers.Count,
						this._Buffers.Length
					});
				}
			}
		}

		public int GetPosition()
		{
			int num = 0;
			int num2;
			AL.GetSource(this._Source, ALGetSourcei.SourceState, out num2);
			if (num2 != 4114)
			{
				for (int i = 0; i < this._BufferBytes.Length; i++)
				{
					this._SamplesSent += this._BufferBytes[i] >> snd.shm.samplebits / 8 - 1;
					this._BufferBytes[i] = 0;
				}
				this._SamplesSent &= snd.shm.samples - 1;
			}
			else
			{
				AL.GetSource(this._Source, ALGetSourcei.SampleOffset, out num);
			}
			return this._SamplesSent + num & snd.shm.samples - 1;
		}

		private const int AL_BUFFER_COUNT = 24;

		private const int BUFFER_SIZE = 65536;

		private bool _IsInitialized;

		private AudioContext _Context;

		private int _Source;

		private int[] _Buffers;

		private int[] _BufferBytes;

		private ALFormat _BufferFormat;

		private int _SamplesSent;

		private Queue<int> _FreeBuffers;
	}
}
