using System.Globalization;

namespace Connected;

public class Converter<TSourceType, TDestinationType>
{
	public event EventHandler<string?>? ErrorOccured;

	/// <summary>
	/// The culture info being used for decimal points, date and time format, etc.
	/// </summary>
	public CultureInfo Culture { get; set; } = Converters.DefaultCulture;

	public TDestinationType? Convert(TSourceType value)
	{
		try
		{
			return ConvertValue(value);
		}
		catch (Exception e)
		{
			TriggerError($"Conversion from {typeof(TSourceType).Name} to {typeof(TDestinationType).Name} failed: {e.Message}", e);
		}

		return default;
	}

	protected virtual TDestinationType? ConvertValue(TSourceType? value)
	{
		return default;
	}

	public TSourceType? ConvertBack(TDestinationType value)
	{
		try
		{
			return ConvertValueBack(value);
		}
		catch (Exception e)
		{
			TriggerError($"Conversion from {typeof(TDestinationType).Name} to {typeof(TSourceType).Name} failed: {e.Message}", e);
		}

		return default;
	}

	protected virtual TSourceType? ConvertValueBack(TDestinationType? value)
	{
		return default;
	}

	protected void TriggerError(string? msg, Exception? e = null)
	{
		ErrorOccured?.Invoke(this, msg);

		OnErrorOccured(msg, e);
	}

	protected virtual void OnErrorOccured(string? msg, Exception? e)
	{

	}
}

/// <summary>
/// Converts values using the supplied lambda functions.
/// </summary>
/// <typeparam name="TSourceType">The source type</typeparam>
/// <typeparam name="TDestinationType">The destination type</typeparam>
public class LambdaConverter<TSourceType, TDestinationType> : Converter<TSourceType, TDestinationType>
{
	private readonly Func<TSourceType, TDestinationType?>? _convertFunction;
	private readonly Func<TDestinationType, TSourceType?>? _convertBackFunction;

	public LambdaConverter(Func<TSourceType, TDestinationType?>? convertFunction = null, Func<TDestinationType, TSourceType?>? convertBackFunction = null)
	{
		_convertFunction = convertFunction;
		_convertBackFunction = convertBackFunction;
	}

	protected override TDestinationType? ConvertValue(TSourceType? value)
	{
		if (_convertFunction is null)
			return base.ConvertValue(value);

		return _convertFunction.Invoke(value);
	}

	protected override TSourceType? ConvertValueBack(TDestinationType? value)
	{
		if (_convertFunction is null)
			return base.ConvertValueBack(value);

		return _convertBackFunction.Invoke(value);
	}
}

/// <summary>
/// Converter from T to string
///
/// Set converts to string
/// Get converts from string
/// </summary>
public class ToStringConverter<T> : Converter<T, string>
{

	/// <summary>
	/// Custom Format to be applied on bidirectional way.
	/// </summary>
	public string? Format { get; set; } = null;
}
