#nullable enable
using System;
using System.Globalization;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Maths;

namespace Robust.Client.ViewVariables.Editors
{
    internal sealed class VVPropEditorNumeric : VVPropEditor
    {
        private readonly NumberType _type;

        public VVPropEditorNumeric(NumberType type)
        {
            _type = type;
        }

        protected override Control MakeUI(object? value)
        {
            var lineEdit = new LineEdit
            {
                Text = NumberToText(value!),
                Editable = !ReadOnly,
                MinSize = new Vector2(240, 0)
            };
            lineEdit.OnTextEntered += e =>
            {
                if (TryTextToNumber(e.Text, _type, out var val))
                    ValueChanged(val);
            };
            return lineEdit;
        }

        private static bool TryTextToNumber(string text, NumberType type, out object? val)
        {
            try
            {
                val = type switch
                {
                    NumberType.Byte => byte.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.SByte => sbyte.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.UShort => ushort.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Short => short.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.UInt => uint.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Int => int.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.ULong => ulong.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Long => long.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Float => float.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Double => double.Parse(text, CultureInfo.InvariantCulture),
                    NumberType.Decimal => decimal.Parse(text, CultureInfo.InvariantCulture),
                    _ => throw new ArgumentOutOfRangeException(),
                };
                return true;
            }
            catch (FormatException)
            {
                val = null;
                return false;
            }
        }

        private static string NumberToText(object val)
        {
            // All numeric primitives implement IConvertible.
            var convert = (IConvertible) val;
            return convert.ToString(CultureInfo.InvariantCulture);
        }

        public enum NumberType : byte
        {
            Byte,
            SByte,
            UShort,
            Short,
            UInt,
            Int,
            ULong,
            Long,
            Float,
            Double,
            Decimal,
        }
    }
}
