using System;
using BizHawk.Emulation.Common;

namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
	public partial class Gameboy : ISoundProvider
	{
		public bool CanProvideAsync => false;

		public void DiscardSamples()
		{
			_soundoutbuffcontains = 0;
		}

		public void GetSamplesSync(out short[] samples, out int nsamp)
		{
			samples = _soundoutbuff;
			nsamp = _soundoutbuffcontains;
		}

		public void SetSyncMode(SyncSoundMode mode)
		{
			if (mode == SyncSoundMode.Async)
			{
				throw new NotSupportedException("Async mode is not supported.");
			}
		}

		public SyncSoundMode SyncMode => SyncSoundMode.Sync;

		public void GetSamplesAsync(short[] samples)
		{
			throw new InvalidOperationException("Async mode is not supported.");
		}

		internal bool Muted => _settings.Muted;

		// sample pairs before resampling
		private readonly short[] _soundbuff = new short[(35112 + 2064) * 2];

		private int _soundoutbuffcontains = 0;

		private readonly short[] _soundoutbuff = new short[2048];

		private int _latchL = 0;
		private int _latchR = 0;

		private BlipBuffer _blipL, _blipR;
		private uint _blipAccumulate;

		private void ProcessSound(int nsamp)
		{
			for (uint i = 0; i < nsamp; i++)
			{
				int curr = _soundbuff[i * 2];

				if (curr != _latchL)
				{
					int diff = _latchL - curr;
					_latchL = curr;
					_blipL.AddDelta(_blipAccumulate, diff >> 2);
				}

				curr = _soundbuff[(i * 2) + 1];

				if (curr != _latchR)
				{
					int diff = _latchR - curr;
					_latchR = curr;
					_blipR.AddDelta(_blipAccumulate, diff >> 2);
				}

				_blipAccumulate++;
			}
		}

		private void ProcessSoundEnd()
		{
			_blipL.EndFrame(_blipAccumulate);
			_blipR.EndFrame(_blipAccumulate);
			_blipAccumulate = 0;

			_soundoutbuffcontains = _blipL.SamplesAvailable();
			if (_soundoutbuffcontains != _blipR.SamplesAvailable())
			{
				throw new InvalidOperationException("Audio processing error");
			}

			_blipL.ReadSamplesLeft(_soundoutbuff, _soundoutbuffcontains);
			_blipR.ReadSamplesRight(_soundoutbuff, _soundoutbuffcontains);
		}

		private void InitSound()
		{
			_blipL = new BlipBuffer(1024);
			_blipL.SetRates(TICKSPERSECOND, 44100);
			_blipR = new BlipBuffer(1024);
			_blipR.SetRates(TICKSPERSECOND, 44100);
		}

		private void DisposeSound()
		{
			if (_blipL != null)
			{
				_blipL.Dispose();
				_blipL = null;
			}

			if (_blipR != null)
			{
				_blipR.Dispose();
				_blipR = null;
			}
		}
	}
}
