using Sandbox;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace VeloX.Audio;

[JsonConverter( typeof( ControllerConverter ) )]
public sealed class Controller
{
	public enum InputTypes
	{
		Throttle,
		RpmFraction,
	}
	public enum OutputTypes
	{
		Volume,
		Pitch,
	}

	public required InputTypes InputParameter { get; set; }
	[Property] public ValueRange InputRange { get; set; }
	public required OutputTypes OutputParameter { get; set; }
	public ValueRange OutputRange { get; set; }
}

public record struct ValueRange
{
	public ValueRange( float inputMin, float inputMax )
	{
		Min = inputMin;
		Max = inputMax;
	}

	[Range( 0, 1 )] public float Min { get; set; }
	[Range( 0, 1 )] public float Max { get; set; }

	public readonly float Remap( float value, float newMin, float newMax )
	{
		var normalized = (value - Min) / (Max - Min);
		return newMin + normalized * (newMax - newMin);
	}
};

public sealed class ControllerConverter : JsonConverter<Controller>
{
	public override Controller Read( ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options )
	{
		if ( reader.TokenType != JsonTokenType.StartArray )
			throw new JsonException( "Expected array for Controller" );

		reader.Read();
		string inputParam = reader.GetString() ?? string.Empty;
		reader.Read();
		float inputMin = reader.GetSingle();
		reader.Read();
		float inputMax = reader.GetSingle();
		reader.Read();
		string outputParam = reader.GetString() ?? string.Empty;
		reader.Read();
		float outputMin = reader.GetSingle();
		reader.Read();
		float outputMax = reader.GetSingle();
		reader.Read(); // End of array

		return new Controller
		{
			InputParameter = Enum.Parse<Controller.InputTypes>( inputParam, true ),
			InputRange = new ValueRange( inputMin, inputMax ),
			OutputParameter = Enum.Parse<Controller.OutputTypes>( outputParam, true ),
			OutputRange = new ValueRange( outputMin, outputMax )
		};
	}

	public override void Write( Utf8JsonWriter writer, Controller value, JsonSerializerOptions options )
	{
		writer.WriteStartArray();
		writer.WriteStringValue( value.InputParameter.ToString() );
		writer.WriteNumberValue( value.InputRange.Min );
		writer.WriteNumberValue( value.InputRange.Max );
		writer.WriteStringValue( value.OutputParameter.ToString() );
		writer.WriteNumberValue( value.OutputRange.Min );
		writer.WriteNumberValue( value.OutputRange.Max );
		writer.WriteEndArray();
	}
}
