EnumUtils
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization;
using System.Text;
namespace Newtonsoft.Json.Utilities
{
    internal static class EnumUtils
    {
        private const char EnumSeparatorChar = ',';
        private const string EnumSeparatorString = ", ";
        private static readonly ThreadSafeStore<Type, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, EnumInfo>(InitializeValuesAndNames);
        private static EnumInfo InitializeValuesAndNames(Type enumType)
        {
            string[] names = Enum.GetNames(enumType);
            string[] array = new string[names.Length];
            ulong[] array2 = new ulong[names.Length];
            for (int i = 0; i < names.Length; i++) {
                string name = names[i];
                FieldInfo field = enumType.GetField(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
                array2[i] = ToUInt64(field.GetValue(null));
                string text = (from EnumMemberAttribute a in field.GetCustomAttributes(typeof(EnumMemberAttribute), true)
                select a.Value).SingleOrDefault() ?? field.Name;
                if (Array.IndexOf(array, text, 0, i) != -1)
                    throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, text, enumType.Name));
                array[i] = text;
            }
            return new EnumInfo(enumType.IsDefined(typeof(FlagsAttribute), false), array2, names, array);
        }
        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 camelCaseText, out string name)
        {
            EnumInfo enumInfo = ValuesAndNamesPerEnum.Get(enumType);
            ulong num = ToUInt64(value);
            if (!enumInfo.IsFlags) {
                int num2 = Array.BinarySearch(enumInfo.Values, num);
                if (num2 >= 0) {
                    name = enumInfo.ResolvedNames[num2];
                    if (camelCaseText)
                        name = StringUtils.ToCamelCase(name);
                    return true;
                }
                name = null;
                return false;
            }
            name = InternalFlagsFormat(enumInfo, num, camelCaseText);
            return name != null;
        }
        private static string InternalFlagsFormat(EnumInfo entry, ulong result, bool camelCaseText)
        {
            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 text = resolvedNames[num];
                    stringBuilder.Insert(0, camelCaseText ? StringUtils.ToCamelCase(text) : text);
                    flag = false;
                }
                num--;
            }
            string text2;
            if (result != 0)
                text2 = null;
            else if (num2 == 0) {
                if (values.Length != 0 && values[0] == 0) {
                    text2 = resolvedNames[0];
                    if (camelCaseText)
                        text2 = StringUtils.ToCamelCase(text2);
                } else
                    text2 = null;
            } else {
                text2 = stringBuilder.ToString();
            }
            return text2;
        }
        public static EnumInfo GetEnumValuesAndNames(Type enumType)
        {
            return ValuesAndNamesPerEnum.Get(enumType);
        }
        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, 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(enumType);
            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;
        }
    }
}