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 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 text = names[i];
				FieldInfo field = enumType.GetField(text, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				array2[i] = EnumUtils.ToUInt64(field.GetValue(null));
				string text2 = (from EnumMemberAttribute a in field.GetCustomAttributes(typeof(EnumMemberAttribute), true)
					select a.Value).SingleOrDefault<string>() ?? field.Name;
				if (Array.IndexOf<string>(array, text2, 0, i) != -1)
				{
					throw new InvalidOperationException("Enum name '{0}' already exists on enum '{1}'.".FormatWith(CultureInfo.InvariantCulture, text2, enumType.Name));
				}
				array[i] = text2;
			}
			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 = EnumUtils.ToUInt64(value);
			EnumInfo enumValuesAndNames = EnumUtils.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 != 0UL)
				{
					list.Add((T)((object)Convert.ChangeType(num2, underlyingType, CultureInfo.CurrentCulture)));
				}
			}
			if (list.Count == 0)
			{
				if (enumValuesAndNames.Values.Any((ulong v) => v == 0UL))
				{
					list.Add(default(T));
				}
			}
			return list;
		}

		public static bool TryToString(Type enumType, object value, bool camelCaseText, out string name)
		{
			EnumInfo enumInfo = EnumUtils.ValuesAndNamesPerEnum.Get(enumType);
			ulong num = EnumUtils.ToUInt64(value);
			if (enumInfo.IsFlags)
			{
				name = EnumUtils.InternalFlagsFormat(enumInfo, num, camelCaseText);
				return name != null;
			}
			int num2 = Array.BinarySearch<ulong>(enumInfo.Values, num);
			if (num2 >= 0)
			{
				name = enumInfo.ResolvedNames[num2];
				if (camelCaseText)
				{
					name = StringUtils.ToCamelCase(name);
				}
				return true;
			}
			name = null;
			return false;
		}

		private static string InternalFlagsFormat(object 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] != 0UL))
			{
				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 != 0UL)
			{
				text2 = null;
			}
			else if (num2 == 0UL)
			{
				if (values.Length != 0 && values[0] == 0UL)
				{
					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 EnumUtils.ValuesAndNamesPerEnum.Get(enumType);
		}

		private static ulong ToUInt64(object value)
		{
			bool flag;
			switch (ConvertUtils.GetTypeCode(value.GetType(), out flag))
			{
			case PrimitiveTypeCode.Char:
				return (ulong)((char)value);
			case PrimitiveTypeCode.Boolean:
				return (ulong)Convert.ToByte((bool)value);
			case PrimitiveTypeCode.SByte:
				return (ulong)((long)((sbyte)value));
			case PrimitiveTypeCode.Int16:
				return (ulong)((long)((short)value));
			case PrimitiveTypeCode.UInt16:
				return (ulong)((ushort)value);
			case PrimitiveTypeCode.Int32:
				return (ulong)((long)((int)value));
			case PrimitiveTypeCode.Byte:
				return (ulong)((byte)value);
			case PrimitiveTypeCode.UInt32:
				return (ulong)((uint)value);
			case PrimitiveTypeCode.Int64:
				return (ulong)((long)value);
			case PrimitiveTypeCode.UInt64:
				return (ulong)value;
			}
			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 = EnumUtils.ValuesAndNamesPerEnum.Get(enumType);
			string[] names = enumInfo.Names;
			string[] resolvedNames = enumInfo.ResolvedNames;
			ulong[] values = enumInfo.Values;
			int? num = EnumUtils.FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.Ordinal);
			if (num != null)
			{
				return Enum.ToObject(enumType, values[num.Value]);
			}
			int num2 = -1;
			int i = 0;
			while (i < value.Length)
			{
				if (char.IsWhiteSpace(value[i]))
				{
					i++;
				}
				else
				{
					num2 = i;
					IL_00A4:
					if (num2 == -1)
					{
						throw new ArgumentException("Must specify valid information for parsing in the string.");
					}
					char c = value[num2];
					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 num3 = 0UL;
					int j = num2;
					while (j <= value.Length)
					{
						int num4 = value.IndexOf(',', j);
						if (num4 == -1)
						{
							num4 = value.Length;
						}
						int num5 = num4;
						while (j < num4 && char.IsWhiteSpace(value[j]))
						{
							j++;
						}
						while (num5 > j && char.IsWhiteSpace(value[num5 - 1]))
						{
							num5--;
						}
						int num6 = num5 - j;
						num = EnumUtils.MatchName(value, names, resolvedNames, j, num6, StringComparison.Ordinal);
						if (num == null)
						{
							num = EnumUtils.MatchName(value, names, resolvedNames, j, num6, StringComparison.OrdinalIgnoreCase);
						}
						if (num != null)
						{
							num3 |= values[num.Value];
							j = num4 + 1;
						}
						else
						{
							num = EnumUtils.FindIndexByName(resolvedNames, value, 0, value.Length, StringComparison.OrdinalIgnoreCase);
							if (num == null)
							{
								throw new ArgumentException("Requested value '{0}' was not found.".FormatWith(CultureInfo.InvariantCulture, value));
							}
							return Enum.ToObject(enumType, values[num.Value]);
						}
					}
					return Enum.ToObject(enumType, num3);
				}
			}
			goto IL_00A4;
		}

		private static int? MatchName(object value, object enumNames, object resolvedNames, int valueIndex, int valueSubstringLength, StringComparison comparison)
		{
			int? num = EnumUtils.FindIndexByName(resolvedNames, value, valueIndex, valueSubstringLength, comparison);
			if (num == null)
			{
				num = EnumUtils.FindIndexByName(enumNames, value, valueIndex, valueSubstringLength, comparison);
			}
			return num;
		}

		private static int? FindIndexByName(object enumNames, object 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 new int?(i);
				}
			}
			return null;
		}

		private static readonly ThreadSafeStore<Type, EnumInfo> ValuesAndNamesPerEnum = new ThreadSafeStore<Type, EnumInfo>(new Func<Type, EnumInfo>(EnumUtils.InitializeValuesAndNames));
	}
}
