using System;

namespace MadMilkman.Ini
{
    delegate bool TryParseDelegate<TDelegate>(string value, out TDelegate result);

    internal static class IniValueParser<T>
    {
        private static TryParseDelegate<T> parser;

        static IniValueParser() { InitializeParser(typeof(T)); }

        private static void InitializeParser(Type type)
        {
            if (type.IsEnum) SetParser<T>(EnumTryParse);
            else if (type == typeof(TimeSpan)) SetParser<TimeSpan>(TimeSpan.TryParse);
            else
                switch (Type.GetTypeCode(type))
                {
                    case TypeCode.Boolean: SetParser<bool>(bool.TryParse); break;
                    case TypeCode.Byte: SetParser<byte>(byte.TryParse); break;
                    case TypeCode.SByte: SetParser<sbyte>(sbyte.TryParse); break;
                    case TypeCode.Int16: SetParser<short>(short.TryParse); break;
                    case TypeCode.UInt16: SetParser<ushort>(ushort.TryParse); break;
                    case TypeCode.Int32: SetParser<int>(int.TryParse); break;
                    case TypeCode.UInt32: SetParser<uint>(uint.TryParse); break;
                    case TypeCode.Int64: SetParser<long>(long.TryParse); break;
                    case TypeCode.UInt64: SetParser<ulong>(ulong.TryParse); break;
                    case TypeCode.Single: SetParser<float>(float.TryParse); break;
                    case TypeCode.Double: SetParser<double>(double.TryParse); break;
                    case TypeCode.Decimal: SetParser<decimal>(decimal.TryParse); break;
                    case TypeCode.Char: SetParser<char>(char.TryParse); break;
                    case TypeCode.DateTime: SetParser<DateTime>(DateTime.TryParse); break;
                    case TypeCode.String: SetParser<string>((string value, out string result) => { result = value; return true; }); break;
                }
        }

        private static void SetParser<TParser>(TryParseDelegate<TParser> parser)
        {
            IniValueParser<TParser>.parser = parser;
        }

        private static bool EnumTryParse<TEnum>(string value, out TEnum result)
        {
            /* REMARKS: To support case insensitivity instead of Enum.IsDefined use Enum.GetNames
             *          to achieve case insensitive string comparison between enum's names and value. */

            Type type = typeof(TEnum);
            if (Enum.IsDefined((type), value))
            {
                result = (TEnum)Enum.Parse(type, value);
                return true;
            }

            result = default(TEnum);
            return false;
        }

        public static bool TryParse(string value, out T result)
        {
            if (parser == null)
                throw new NotSupportedException();

            return parser(value, out result);
        }
    }
}