MethodBinder
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace Newtonsoft.Json.Utilities
{
    [System.Runtime.CompilerServices.NullableContext(1)]
    [System.Runtime.CompilerServices.Nullable(0)]
    internal static class MethodBinder
    {
        [System.Runtime.CompilerServices.Nullable(0)]
        private class ParametersMatchComparer : IComparer<ParameterInfo[]>
        {
            private readonly IList<Type> _types;
            private readonly bool _enableParamArray;
            public ParametersMatchComparer(IList<Type> types, bool enableParamArray)
            {
                ValidationUtils.ArgumentNotNull(types, "types");
                _types = types;
                _enableParamArray = enableParamArray;
            }
            public int Compare(ParameterInfo[] parameters1, ParameterInfo[] parameters2)
            {
                ValidationUtils.ArgumentNotNull(parameters1, "parameters1");
                ValidationUtils.ArgumentNotNull(parameters2, "parameters2");
                if (parameters1.Length == 0)
                    return -1;
                if (parameters2.Length == 0)
                    return 1;
                Type type = null;
                Type type2 = null;
                if (_enableParamArray) {
                    ParameterInfo parameterInfo = parameters1[parameters1.Length - 1];
                    if (parameterInfo.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo, typeof(ParamArrayAttribute)))
                        type = parameterInfo.ParameterType.GetElementType();
                    ParameterInfo parameterInfo2 = parameters2[parameters2.Length - 1];
                    if (parameterInfo2.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo2, typeof(ParamArrayAttribute)))
                        type2 = parameterInfo2.ParameterType.GetElementType();
                    if ((object)type != null && (object)type2 == null)
                        return 1;
                    if ((object)type2 != null && (object)type == null)
                        return -1;
                }
                for (int i = 0; i < _types.Count; i++) {
                    Type type3 = ((object)type != null && i >= parameters1.Length - 1) ? type : parameters1[i].ParameterType;
                    Type type4 = ((object)type2 != null && i >= parameters2.Length - 1) ? type2 : parameters2[i].ParameterType;
                    if ((object)type3 != type4) {
                        if ((object)type3 == _types[i])
                            return -1;
                        if ((object)type4 == _types[i])
                            return 1;
                        int num = ChooseMorePreciseType(type3, type4);
                        if (num != 0)
                            return num;
                    }
                }
                return 0;
            }
            private static int ChooseMorePreciseType(Type type1, Type type2)
            {
                if (type1.IsByRef || type2.IsByRef) {
                    if (type1.IsByRef && type2.IsByRef) {
                        type1 = type1.GetElementType();
                        type2 = type2.GetElementType();
                    } else if (type1.IsByRef) {
                        type1 = type1.GetElementType();
                        if ((object)type1 == type2)
                            return 1;
                    } else {
                        type2 = type2.GetElementType();
                        if ((object)type2 == type1)
                            return -1;
                    }
                }
                bool flag;
                bool flag2;
                if (type1.IsPrimitive() && type2.IsPrimitive()) {
                    flag = CanConvertPrimitive(type2, type1);
                    flag2 = CanConvertPrimitive(type1, type2);
                } else {
                    flag = TypeExtensions.IsAssignableFrom(type1, type2);
                    flag2 = TypeExtensions.IsAssignableFrom(type2, type1);
                }
                if (flag == flag2)
                    return 0;
                if (!flag)
                    return -1;
                return 1;
            }
        }
        private static readonly Type[] PrimitiveTypes = new Type[12] {
            typeof(bool),
            typeof(char),
            typeof(sbyte),
            typeof(byte),
            typeof(short),
            typeof(ushort),
            typeof(int),
            typeof(uint),
            typeof(long),
            typeof(ulong),
            typeof(float),
            typeof(double)
        };
        private static readonly int[] WideningMasks = new int[12] {
            1,
            4066,
            3412,
            4090,
            3408,
            4066,
            3392,
            3968,
            3328,
            3584,
            3072,
            2048
        };
        private static bool CanConvertPrimitive(Type from, Type to)
        {
            if ((object)from == to)
                return true;
            int num = 0;
            int num2 = 0;
            for (int i = 0; i < PrimitiveTypes.Length; i++) {
                if ((object)PrimitiveTypes[i] == from)
                    num = WideningMasks[i];
                else if ((object)PrimitiveTypes[i] == to) {
                    num2 = 1 << i;
                }
                if (num != 0 && num2 != 0)
                    break;
            }
            return (num & num2) != 0;
        }
        private static bool FilterParameters(ParameterInfo[] parameters, IList<Type> types, bool enableParamArray)
        {
            ValidationUtils.ArgumentNotNull(parameters, "parameters");
            ValidationUtils.ArgumentNotNull(types, "types");
            if (parameters.Length == 0)
                return types.Count == 0;
            if (parameters.Length > types.Count)
                return false;
            Type type = null;
            if (enableParamArray) {
                ParameterInfo parameterInfo = parameters[parameters.Length - 1];
                if (parameterInfo.ParameterType.IsArray && CustomAttributeExtensions.IsDefined(parameterInfo, typeof(ParamArrayAttribute)))
                    type = parameterInfo.ParameterType.GetElementType();
            }
            if ((object)type == null && parameters.Length != types.Count)
                return false;
            for (int i = 0; i < types.Count; i++) {
                Type type2 = ((object)type != null && i >= parameters.Length - 1) ? type : parameters[i].ParameterType;
                if ((object)type2 != types[i] && (object)type2 != typeof(object)) {
                    if (type2.IsPrimitive()) {
                        if (!types[i].IsPrimitive() || !CanConvertPrimitive(types[i], type2))
                            return false;
                    } else if (!TypeExtensions.IsAssignableFrom(type2, types[i])) {
                        return false;
                    }
                }
            }
            return true;
        }
        public static TMethod SelectMethod<[System.Runtime.CompilerServices.Nullable(0)] TMethod>(IEnumerable<TMethod> candidates, IList<Type> types) where TMethod : MethodBase
        {
            ValidationUtils.ArgumentNotNull(candidates, "candidates");
            ValidationUtils.ArgumentNotNull(types, "types");
            return (from m in candidates
            where FilterParameters(m.GetParameters(), types, false)
            select m).OrderBy((TMethod m) => m.GetParameters(), new ParametersMatchComparer(types, false)).FirstOrDefault();
        }
    }
}