using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace BizHawk.Common
{
	public class DescribableEnumConverter : EnumConverter
	{
		private readonly Type enumType;

		public DescribableEnumConverter(Type type) : base(type)
		{
			enumType = type;
		}

		public override bool CanConvertFrom(ITypeDescriptorContext context, Type srcType) => srcType == typeof(string);

		public override bool CanConvertTo(ITypeDescriptorContext context, Type destType) => destType == typeof(string);

		public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
		{
			var valueStr = value?.ToString() ?? throw new ArgumentException($"got null {nameof(value)}");
			return Enum.Parse(
				enumType,
				enumType.GetFields(BindingFlags.Public | BindingFlags.Static)
					.FirstOrDefault(fi => valueStr.Equals((fi.GetCustomAttribute(typeof(DisplayAttribute)) as DisplayAttribute)?.Name))?.Name
					?? valueStr
			);
		}

		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
		{
			var fieldName = Enum.GetName(enumType, value ?? throw new ArgumentException($"got null {nameof(value)}"));
			if (fieldName != null)
			{
				var fieldInfo = enumType.GetField(fieldName);
				if (fieldInfo != null)
				{
					var found = fieldInfo.GetCustomAttribute(typeof(DisplayAttribute));
					if (found is DisplayAttribute da) return da.Name;
				}
			}
#pragma warning disable CS8603 // value can't be null here, it would've thrown already
			return value.ToString();
#pragma warning restore CS8603
		}

		public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context) => new StandardValuesCollection(
			enumType.GetFields(BindingFlags.Public | BindingFlags.Static)
				.Select(fi => fi.GetValue(null))
				.ToList()
		);

		public override bool GetStandardValuesExclusive(ITypeDescriptorContext context) => true;

		public override bool GetStandardValuesSupported(ITypeDescriptorContext context) => true;
	}
}
