private static Type[] GetArgTypes(SerializedProperty argProp, SerializedSystemFunc.Hidden.Arg argsTemplateToUse)
 {
     if (argProp != null)
     {
         Type[] types = new Type[argProp.arraySize];
         for (int i = 0; i < argProp.arraySize; i++)
         {
             types[i] = argsTemplateToUse.RealType(
                 (SerializedSystemFunc.Hidden.Arg.ArgType)
                 argProp.FindPropertyRelative("Array.data[" + i + "]." + nameof(SerializedSystemFunc.Hidden.Arg.argType)).enumValueIndex
                 );
         }
         return(types);
     }
     else
     {
         UnityEngine.Debug.LogWarning("argProp is null!! ");
         return(null);
     }
 }
    public override void OnGUI(Rect position, SerializedProperty serCallBaseProperty, GUIContent label)
    {
        // Indent label
        label.text = " " + label.text;

        GUI.Box(position, "", (GUIStyle)"flow overlay box");
        position.y += 4;
        // Using BeginProperty / EndProperty on the parent property means that
        // prefab override logic works on the entire property.
        EditorGUI.BeginProperty(position, label, serCallBaseProperty);

        // Draw label
        Rect pos = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);

        Rect targetRect = new Rect(pos.x, pos.y, pos.width, EditorGUIUtility.singleLineHeight);

        // Get target
        SerializedProperty targetProp = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase._target));
        object             target     = targetProp.objectReferenceValue;

        // Get UdId
        SerializedProperty UdiDProp = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase.UDIDObject));

        if (target != null)
        {
            if (string.IsNullOrEmpty(UdiDProp.stringValue))
            {
                UdiDProp.stringValue = target.GetHashCode().ToString();
                serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase.dirty)).boolValue        = true;
                serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase.dirtyChecker)).boolValue = false;

                serCallBaseProperty.serializedObject.ApplyModifiedProperties();
                serCallBaseProperty.serializedObject.Update();
                return;
            }
        }
        else
        {
            UdiDProp.stringValue = null;
        }



        SerializedSystemFunc.Hidden.Arg argsTemplateTouse = new SerializedSystemFunc.Hidden.Arg();

        if (IpropGenerator != null)
        {
            if (!IpropGenerator.GetType().FullName.EndsWith(serCallBaseProperty.type))
            {
                IpropGenerator = null;
            }
        }

        if (IpropGenerator == null)
        {
            // UnityEngine.Debug.LogWarning(" Working For : " + serCallBaseProperty.type);
            var typesFromAssymbly = (from type in typeof(SerializedSystemFunc.Hidden.SerializableCallbackBase).Assembly.GetTypes()
                                     where type.FullName.EndsWith(serCallBaseProperty.type)
                                     select type);

            if (typesFromAssymbly != null && typesFromAssymbly.Count() > 0)
            {
                foreach (var item in typesFromAssymbly)
                {
                    IpropGenerator = ((SerializedSystemFunc.Hidden.SerializableCallbackBase)System.Activator.CreateInstance(item));
                    break;
                }
            }
        }
        argsTemplateTouse = IpropGenerator.GenTemplateArg();


        EditorGUI.PropertyField(targetRect, targetProp, GUIContent.none);

        if (target != null)
        {
            // Get method name
            SerializedProperty methodProp = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase._methodName));
            string             methodName = methodProp.stringValue;

            // Get args
            SerializedProperty argProps = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase._args));
            Type[]             argTypes = GetArgTypes(argProps, argsTemplateTouse);

            // Get active method
            MethodInfo activeMethod = GetMethod(target, methodName, argTypes);

            GUIContent methodlabel = new GUIContent("n/a");
            if (activeMethod != null)
            {
                methodlabel = new GUIContent(PrettifyMethod(activeMethod));
            }
            else if (!string.IsNullOrEmpty(methodName))
            {
                methodlabel = new GUIContent("Missing (" + PrettifyMethod(methodName, argTypes) + ")");
            }

            Rect methodRect = new Rect(position.x, targetRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width, EditorGUIUtility.singleLineHeight);

            // Method select button
            pos = EditorGUI.PrefixLabel(methodRect, GUIUtility.GetControlID(FocusType.Passive), new GUIContent(" Method"));  // dynamic ? "Method (dynamic)" : "Method"
            if (EditorGUI.DropdownButton(pos, methodlabel, FocusType.Keyboard))
            {
                MethodSelector(serCallBaseProperty, argsTemplateTouse, IpropGenerator);
            }
        }
        else
        {
            // Target is null!
            Rect   helpBoxRect = new Rect(position.x + 8, targetRect.max.y + EditorGUIUtility.standardVerticalSpacing, position.width - 16, EditorGUIUtility.singleLineHeight);
            string msg         = "Call not set. Execution will be slower.";
            EditorGUI.LabelField(helpBoxRect, new GUIContent(msg, msg), "helpBox");
        }

        // Set indent back to what it was
        EditorGUI.EndProperty();
    }
    private static void MethodSelector(SerializedProperty serCallBaseProperty, SerializedSystemFunc.Hidden.Arg argsTemplateToUse, SerializedSystemFunc.Hidden.SerializableCallbackBase coreProp)
    {
        // Return type constraint
        Type returnType = null;

        // Arg type constraint
        Type[] argTypes = new Type[0];

        // Get return type and argument constraints
        string stringValueTypedName = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase._typeName)).stringValue;
        Type   MainTypeToLook       = Type.GetType(stringValueTypedName, false);

        Type[] genericTypes = null;
        if (MainTypeToLook != null)
        {
            genericTypes = MainTypeToLook.BaseType.GetGenericArguments();
        }

        if (genericTypes != null && genericTypes.Length > 0)
        {
            // The last generic argument is the return type
            returnType = genericTypes[genericTypes.Length - 1];
            if (genericTypes.Length > 1)
            {
                argTypes = new Type[genericTypes.Length - 1];
                Array.Copy(genericTypes, argTypes, genericTypes.Length - 1);
            }
        }

        Type[] typesConstraintsParams = coreProp.GetConstraints();


        SerializedProperty targetProp = serCallBaseProperty.FindPropertyRelative(nameof(SerializedSystemFunc.Hidden.SerializableCallbackBase._target));

        List <MenuItem> dynamicItems = new List <MenuItem>();
        List <MenuItem> staticItems  = new List <MenuItem>();

        List <Object> targets = new List <Object>()
        {
            targetProp.objectReferenceValue
        };

        if (targets[0] is Component)
        {
            targets = (targets[0] as Component).gameObject.GetComponents <Component>().ToList <Object>();
            targets.Add((targetProp.objectReferenceValue as Component).gameObject);
        }
        else if (targets[0] is GameObject)
        {
            targets = (targets[0] as GameObject).GetComponents <Component>().ToList <Object>();
            targets.Add(targetProp.objectReferenceValue as GameObject);
        }

        for (int c = 0; c < targets.Count; c++)
        {
            UnityEngine.Object t       = targets[c];
            MethodInfo[]       methods = targets[c].GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);

            for (int i = 0; i < methods.Length; i++)
            {
                MethodInfo method = methods[i];

                // Skip methods with wrong return type
                if (returnType != null && method.ReturnType != returnType)
                {
                    continue;
                }
                // Skip methods with null return type
                if (method.ReturnType == typeof(void))
                {
                    continue;
                }
                // Skip generic methods
                if (method.IsGenericMethod)
                {
                    continue;
                }


                Type[] parms = method.GetParameters().Select(x => x.ParameterType).ToArray();

                // Skip methods with more than 4 args
                if (parms.Length > 4)
                {
                    continue;
                }

                // Skip methods with unsupported args
                if (parms.Any(x => !argsTemplateToUse.IsSupported(x)))
                {
                    continue;
                }


                // Skip when parameters are defined
                if (typesConstraintsParams != null && typesConstraintsParams.Length > 0 && !parms.Any(x => CheckIfPassing(x, typesConstraintsParams)))
                {
                    continue;
                }

                // Skip methods with wrong constrained args
                if (argTypes.Length > 0 && !Enumerable.SequenceEqual(argTypes, parms))
                {
                    continue;
                }

                // dynamicItems.Add(new MenuItem(targets[c].GetType().Name + "/" + methods[i].DeclaringType.Name,
                //                                methods[i].Name,
                //                                () => SetMethod(serCallBaseProperty, t, method, true)));

                string methodPrettyName = PrettifyMethod(methods[i]);
                staticItems.Add(new MenuItem(targets[c].GetType().Name + "/" + methods[i].DeclaringType.Name,
                                             methodPrettyName,
                                             () => SetMethod(serCallBaseProperty, t, method)));
            }
        }

        // Construct and display context menu
        GenericMenu menu = new GenericMenu();

        if (dynamicItems.Count > 0)
        {
            string[] paths = dynamicItems.GroupBy(x => x.path).Select(x => x.First().path).ToArray();
            foreach (string path in paths)
            {
                menu.AddItem(new GUIContent(path + "/Dynamic " + PrettifyTypes(argTypes)), false, null);
            }
            for (int i = 0; i < dynamicItems.Count; i++)
            {
                menu.AddItem(dynamicItems[i].label, false, dynamicItems[i].action);
            }
            foreach (string path in paths)
            {
                menu.AddItem(new GUIContent(path + "/  "), false, null);
                menu.AddItem(new GUIContent(path + "/Static parameters"), false, null);
            }
        }
        for (int i = 0; i < staticItems.Count; i++)
        {
            menu.AddItem(staticItems[i].label, false, staticItems[i].action);
        }
        if (menu.GetItemCount() == 0)
        {
            menu.AddDisabledItem(new GUIContent("No methods with return type '" + GetSimplePrettyTypeName(returnType) + "'"));
        }
        menu.ShowAsContext();
    }