EnumUtils
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Text;
namespace Newtonsoft.Json.Utilities
{
    [System.Runtime.CompilerServices.NullableContext(1)]
    [System.Runtime.CompilerServices.Nullable(0)]
    internal static class EnumUtils
    {
        private const char EnumSeparatorChar = ',';
        private const string EnumSeparatorString = ", ";
        [System.Runtime.CompilerServices.Nullable(new byte[] {
            1,
            0,
            1,
            2,
            1
        })]
        private static readonly ThreadSafeStore<StructMultiKey<Type, NamingStrategy>, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<StructMultiKey<Type, NamingStrategy>, EnumInfo>(InitializeValuesAndNames);
        private static CamelCaseNamingStrategy _camelCaseNamingStrategy = new CamelCaseNamingStrategy();
        private static EnumInfo InitializeValuesAndNames([System.Runtime.CompilerServices.Nullable(new byte[] {
            0,
            1,
            2
        })] StructMultiKey<Type, NamingStrategy> key)
        {
            Type value = key.Value1;
            string[] names = Enum.GetNames(value);
            string[] array = new string[names.Length];
            ulong[] array2 = new ulong[names.Length];
            for (int i = 0; i < names.Length; i++) {
                string text = names[i];
                FieldInfo field = value.GetField(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                array2[i] = ToUInt64(field.GetValue(null));
                object text2 = (from EnumMemberAttribute a in field.GetCustomAttributes(typeof(EnumMemberAttribute), true)
                select a.Value).SingleOrDefault();
                bool hasSpecifiedName = text2 != null;
                if (text2 == null)
                    text2 = text;
                string text3 = (string)text2;
                if (Array.IndexOf(array, text3, 0, i) != -1)
                    throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, text3, value.Name));
                array[i] = ((key.Value2 != null) ? key.Value2.GetPropertyName(text3, hasSpecifiedName) : text3);
            }
            return new EnumInfo(value.IsDefined(typeof(FlagsAttribute), false), array2, names, array);
        }
        [System.Runtime.CompilerServices.NullableContext(0)]
        [return: System.Runtime.CompilerServices.Nullable(new byte[] {
            1,
            0
        })]
        public static IList<T> GetFlagsValues<T>(T value) where T : struct
        {
            Type typeFromHandle = typeof(T);
            if (!typeFromHandle.IsDefined(typeof(FlagsAttribute), false))
                throw new ArgumentException("Enum type {0} is not a set of flags.".FormatWith(CultureInfo.InvariantCulture, typeFromHandle));
            Type underlyingType = Enum.GetUnderlyingType(value.GetType());
            ulong num = ToUInt64(value);
            EnumInfo enumValuesAndNames = GetEnumValuesAndNames(typeFromHandle);
            IList<T> list = new List<T>();
            for (int i = 0; i < enumValuesAndNames.Values.Length; i++) {
                ulong num2 = enumValuesAndNames.Values[i];
                if ((num & num2) == num2 && num2 != 0)
                    ((ICollection<T>)list).Add((T)Convert.ChangeType(num2, underlyingType, CultureInfo.CurrentCulture));
            }
            if (((ICollection<T>)list).Count == 0 && enumValuesAndNames.Values.Any((ulong v) => v == 0))
                ((ICollection<T>)list).Add(default(T));
            return list;
        }
        public static bool TryToString(Type enumType, object value, bool camelCase, [System.Runtime.CompilerServices.Nullable(2)] [NotNullWhen(true)] out string name)
        {
            return TryToString(enumType, value, camelCase ? _camelCaseNamingStrategy : null, out name);
        }
        public static bool TryToString(Type enumType, object value, [System.Runtime.CompilerServices.Nullable(2)] NamingStrategy namingStrategy, [System.Runtime.CompilerServices.Nullable(2)] [NotNullWhen(true)] out string name)
        {
            EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, namingStrategy));
            ulong num = ToUInt64(value);
            if (!enumInfo.IsFlags) {
                int num2 = Array.BinarySearch(enumInfo.Values, num);
                if (num2 >= 0) {
                    name = enumInfo.ResolvedNames[num2];
                    return true;
                }
                name = null;
                return false;
            }
            name = InternalFlagsFormat(enumInfo, num);
            return name != null;
        }
        [return: System.Runtime.CompilerServices.Nullable(2)]
        private static string InternalFlagsFormat(EnumInfo entry, ulong result)
        {
            string[] resolvedNames = entry.ResolvedNames;
            ulong[] values = entry.Values;
            int num = values.Length - 1;
            StringBuilder stringBuilder = new StringBuilder();
            bool flag = true;
            ulong num2 = result;
            while (num >= 0 && (num != 0 || values[num] != 0)) {
                if ((result & values[num]) == values[num]) {
                    result -= values[num];
                    if (!flag)
                        stringBuilder.Insert(0, ", ");
                    string value = resolvedNames[num];
                    stringBuilder.Insert(0, value);
                    flag = false;
                }
                num--;
            }
            if (result == 0) {
                if (num2 != 0)
                    return stringBuilder.ToString();
                if (values.Length != 0 && values[0] == 0)
                    return resolvedNames[0];
                return null;
            }
            return null;
        }
        public static EnumInfo GetEnumValuesAndNames(Type enumType)
        {
            return ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, null));
        }
        private static ulong ToUInt64(object value)
        {
            bool isEnum;
            switch (ConvertUtils.GetTypeCode(value.GetType(), out isEnum)) {
            case PrimitiveTypeCode.SByte:
                return (ulong)(sbyte)value;
            case PrimitiveTypeCode.Byte:
                return (byte)value;
            case PrimitiveTypeCode.Boolean:
                return Convert.ToByte((bool)value);
            case PrimitiveTypeCode.Int16:
                return (ulong)(short)value;
            case PrimitiveTypeCode.UInt16:
                return (ushort)value;
            case PrimitiveTypeCode.Char:
                return (char)value;
            case PrimitiveTypeCode.UInt32:
                return (uint)value;
            case PrimitiveTypeCode.Int32:
                return (ulong)(int)value;
            case PrimitiveTypeCode.UInt64:
                return (ulong)value;
            case PrimitiveTypeCode.Int64:
                return (ulong)(long)value;
            default:
                throw new InvalidOperationException("Unknown enum type.");
            }
        }
        public static object ParseEnum(Type enumType, [System.Runtime.CompilerServices.Nullable(2)] NamingStrategy namingStrategy, string value, bool disallowNumber)
        {
            ValidationUtils.ArgumentNotNull(enumType, "enumType");
            ValidationUtils.ArgumentNotNull(value, "value");
            if (!enumType.IsEnum())
                throw new ArgumentException("Type provided must be an Enum.", "enumType");
            EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(new StructMultiKey<Type, NamingStrategy>(enumType, namingStrategy));
            string[] names = enumInfo.Names;
            string[] resolvedNames = enumInfo.ResolvedNames;
            ulong[] values = enumInfo.Values;
            int? nullable = FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.Ordinal);
            if (nullable.HasValue)
                return Enum.ToObject(enumType, values[nullable.Value]);
            int num = -1;
            for (int i = 0; i < value.Length; i++) {
                if (!char.IsWhiteSpace(value[i])) {
                    num = i;
                    break;
                }
            }
            if (num == -1)
                throw new ArgumentException("Must specify valid information for parsing in the string.");
            char c = value[num];
            if (char.IsDigit(c) || c == '-' || c == '+') {
                Type underlyingType = Enum.GetUnderlyingType(enumType);
                value = value.Trim();
                object obj = null;
                try {
                    obj = Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
                } catch (FormatException) {
                }
                if (obj != null) {
                    if (disallowNumber)
                        throw new FormatException("Integer string '{0}' is not allowed.".FormatWith(CultureInfo.InvariantCulture, value));
                    return Enum.ToObject(enumType, obj);
                }
            }
            ulong num2 = 0;
            int num3;
            for (int j = num; j <= value.Length; j = num3 + 1) {
                num3 = value.IndexOf(',', j);
                if (num3 == -1)
                    num3 = value.Length;
                int num4 = num3;
                for (; j < num3 && char.IsWhiteSpace(value[j]); j++) {
                }
                while (num4 > j && char.IsWhiteSpace(value[num4 - 1])) {
                    num4--;
                }
                int valueSubstringLength = num4 - j;
                nullable = MatchName(value, names, resolvedNames, j, valueSubstringLength, StringComparison.Ordinal);
                if (!nullable.HasValue)
                    nullable = MatchName(value, names, resolvedNames, j, valueSubstringLength, StringComparison.OrdinalIgnoreCase);
                if (!nullable.HasValue) {
                    nullable = FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.OrdinalIgnoreCase);
                    if (nullable.HasValue)
                        return Enum.ToObject(enumType, values[nullable.Value]);
                    throw new ArgumentException("Requested value '{0}' was not found.".FormatWith(CultureInfo.InvariantCulture, value));
                }
                num2 |= values[nullable.Value];
            }
            return Enum.ToObject(enumType, num2);
        }
        private static int? MatchName(string value, string[] enumNames, string[] resolvedNames, int valueIndex, int valueSubstringLength, StringComparison comparison)
        {
            int? result = FindIndexByName(resolvedNames, value, valueIndex, valueSubstringLength, comparison);
            if (!result.HasValue)
                result = FindIndexByName(enumNames, value, valueIndex, valueSubstringLength, comparison);
            return result;
        }
        private static int? FindIndexByName(string[] enumNames, string value, int valueIndex, int valueSubstringLength, StringComparison comparison)
        {
            for (int i = 0; i < enumNames.Length; i++) {
                if (enumNames[i].Length == valueSubstringLength && string.Compare(enumNames[i], 0, value, valueIndex, valueSubstringLength, comparison) == 0)
                    return i;
            }
            return null;
        }
    }
}