using Sandbox.Audio;

namespace Sandbox;

partial class SoundHandle
{
	private SteamAudioSource _audioSource;
	private Dictionary<Listener, SteamAudioSource> _audioSources;

	internal SteamAudioSource GetSource( Listener listener )
	{
		// Find listener source
		if ( _audioSources?.TryGetValue( listener, out var source ) == true )
		{
			return source;
		}

		// Local audio source
		return _audioSource;
	}

	private void UpdateSources()
	{
		// Only need a single audio source if listen local
		if ( ListenLocal )
		{
			_audioSource ??= new SteamAudioSource();
			_audioSource.UpdateFrom( this );

			// Dispose listener sources if there's any
			if ( _audioSources is not null )
			{
				foreach ( var source in _audioSources.Values )
				{
					source.Dispose();
				}

				_audioSources.Clear();
				_audioSources = default;
			}

			return;
		}

		// Dipose local audio source if we're not listen local
		if ( _audioSource is not null )
		{
			_audioSource?.Dispose();
			_audioSource = null;
		}

		_audioSources ??= new();

		// Remove stale sources
		foreach ( var listener in Listener.Removed )
		{
			if ( _audioSources.Remove( listener, out var source ) )
			{
				source.Dispose();
			}
		}

		// Find listeners of this scene
		var listeners = Listener.Active.Where( x => x.Scene == Scene );

		// Add and update sources
		foreach ( var listener in listeners )
		{
			if ( !_audioSources.TryGetValue( listener, out var source ) )
			{
				source = new SteamAudioSource();
				_audioSources[listener] = source;
			}

			source.UpdateFrom( this, Scene?.PhysicsWorld, listener.Position );
		}
	}

	private void DisposeSources()
	{
		// Dispose local source
		MainThread.QueueDispose( _audioSource );
		_audioSource = default;

		// Dispose listener sources if there's any
		if ( _audioSources is not null )
		{
			foreach ( var source in _audioSources.Values )
			{
				MainThread.QueueDispose( source );
			}

			_audioSources.Clear();
			_audioSources = default;
		}
	}
}
