using Sandbox;
using Sandbox.Audio;
using System;
using System.Collections.Generic;
using VeloX.Audio;
using static VeloX.EngineStream;

namespace VeloX;

public class EngineStreamPlayer( EngineStream stream ) : IDisposable
{

	private static readonly Mixer EngineMixer = Mixer.FindMixerByName( "Car Engine" );
	public EngineStream Stream { get; set; } = stream;

	public EngineState EngineState { get; set; }
	public bool EngineSoundPaused => EngineState != EngineState.Running;

	public float Throttle { get; set; }
	public bool IsRedlining { get; set; }
	public float RPMPercent { get; set; }

	private float _wobbleTime;

	public readonly Dictionary<Layer, SoundHandle> EngineSounds = [];

	public void Update( float deltaTime, Vector3 position, bool isLocal = false )
	{
		
		var globalPitch = 1.0f;

		// Gear wobble effect
		if ( _wobbleTime > 0 )
		{
			_wobbleTime -= deltaTime * (0.1f + Throttle);
			globalPitch += MathF.Cos( _wobbleTime * Stream.Parameters.WobbleFrequency ) * _wobbleTime * (1 - _wobbleTime) * Stream.Parameters.WobbleStrength;
		}

		globalPitch *= Stream.Parameters.Pitch;

		// Redline effect
		var redlineVolume = 1.0f;
		if ( IsRedlining )
		{
			redlineVolume = 1 - Stream.Parameters.RedlineStrength +
						   MathF.Cos( RealTime.Now * Stream.Parameters.RedlineFrequency ) *
						   Stream.Parameters.RedlineStrength;

		}
		// Process layers
		foreach ( var (id, layer) in Stream.Layers )
		{
			EngineSounds.TryGetValue( layer, out var channel );
			
			if ( !channel.IsValid() && layer.AudioPath.IsValid() )
			{
				channel = Sound.PlayFile( layer.AudioPath );
				EngineSounds[layer] = channel;
			}

			if ( !channel.IsValid() || channel.Paused && (EngineSoundPaused || layer.IsMuted) )
				continue;

			// Reset controller outputs
			float layerVolume = 1.0f;
			float layerPitch = 1.0f;

			// Apply all controllers
			foreach ( var controller in layer.Controllers )
			{
				var inputValue = controller.InputParameter switch
				{
					Controller.InputTypes.Throttle => Throttle,
					Controller.InputTypes.RpmFraction => RPMPercent,
					_ => 0.0f
				};

				var normalized = Math.Clamp( inputValue, controller.InputRange.Min, controller.InputRange.Max );
				var outputValue = controller.InputRange.Remap(
					normalized,
					controller.OutputRange.Min,
					controller.OutputRange.Max
				);

				// Apply to correct parameter
				switch ( controller.OutputParameter )
				{
					case Controller.OutputTypes.Volume:
						layerVolume *= outputValue;
						break;
					case Controller.OutputTypes.Pitch:
						layerPitch *= outputValue;
						break;
				}
			}

			// Apply redline effect if needed
			layerVolume *= layer.UseRedline ? redlineVolume : 1.0f;
			layerPitch *= globalPitch;
			// Update audio channel
			channel.Pitch = layerPitch;
			channel.Volume = layerVolume * Stream.Parameters.Volume;
			channel.Position = position;
			channel.ListenLocal = isLocal;
			channel.Paused = EngineSoundPaused || layer.IsMuted;
			channel.TargetMixer = EngineMixer;

		}
	}

	public void Dispose()
	{
		foreach ( var item in EngineSounds )
		{
			item.Value?.Stop();
			item.Value?.Dispose();
		}
	}
}
