// TO DO: CLASS NAME has to include the type of the input parameters if it is a method to allow overloading

        public static string GenerateFile(MemberInfo memberInfo, out string filename)
        {
            List <KeyValuePair <string, Type> > input  = new List <KeyValuePair <string, Type> >();
            List <KeyValuePair <string, Type> > output = new List <KeyValuePair <string, Type> >();

            StringBuilder builder = new StringBuilder();

            builder.Append("//////////////////////////////////////\n");
            builder.Append("//// FILE GENERATED AUTOMATICALLY ////\n");
            builder.Append("//////////////////////////////////////\n");
            builder.Append("\n");
            builder.Append("using System;\n");
            builder.Append("using Ashkatchap.AIBrain.Nodes;\n");
            builder.Append("\n");
            builder.Append("namespace Ashkatchap.AIBrain.GeneratedNodes {\n");
            builder.Append("	[Serializable]\n");
            builder.Append("	[CreateNode(\"Actuator/"+ memberInfo.DeclaringType.FullName.Replace('.', '/') + "/" + TypeFinder.GetDisplayName(memberInfo, false, false) + "\")]\n");
            filename = "GN_" + (memberInfo.DeclaringType.FullName + "_" + memberInfo.Name).Replace('.', '_');
            if (memberInfo.MemberType == MemberTypes.Method)
            {
                string methodSignature = string.Join("__", ((MethodInfo)memberInfo).GetParameters().Select(p => p.ParameterType.FullName.Replace('.', '_')).ToArray());
                filename += "_" + GetHashString(methodSignature);
            }
            filename = filename.Replace("&", "_Out_");
            builder.Append("	public class "+ filename + " : Node {\n");
            filename += ".cs";

            bool useReference = memberInfo.MemberType == MemberTypes.Method && !((MethodInfo)memberInfo).IsStatic;

            useReference |= memberInfo.MemberType == MemberTypes.Field && !((FieldInfo)memberInfo).IsStatic;
            useReference |= memberInfo.MemberType == MemberTypes.Property && !(
                (((PropertyInfo)memberInfo).GetGetMethod(false) != null && ((PropertyInfo)memberInfo).GetGetMethod(false).IsStatic) ||
                (((PropertyInfo)memberInfo).GetSetMethod(false) != null && ((PropertyInfo)memberInfo).GetSetMethod(false).IsStatic)
                );

            if (useReference)
            {
                builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Input_"+ TemplateIO.GetIOName(memberInfo.DeclaringType) + " refObject;\n");
                if (memberInfo.DeclaringType.IsValueType)
                {
                    builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Output_"+ TemplateIO.GetIOName(memberInfo.DeclaringType) + " newRefObject;\n");
                }
            }

            bool isSpecialSetMethod = memberInfo.MemberType == MemberTypes.Method && (memberInfo as MethodInfo).IsSpecialName && memberInfo.Name.StartsWith("set_");

            ParameterInfo[] parameters      = null;
            bool            methodHasReturn = false;
            bool            canWrite        = memberInfo.MemberType == MemberTypes.Field && !((FieldInfo)memberInfo).IsInitOnly && !((FieldInfo)memberInfo).IsLiteral;

            canWrite |= memberInfo.MemberType == MemberTypes.Property && (memberInfo as PropertyInfo).CanWrite;
            canWrite |= isSpecialSetMethod;
            canWrite &= !memberInfo.DeclaringType.IsValueType;
            bool canRead           = memberInfo.MemberType == MemberTypes.Field || (memberInfo.MemberType == MemberTypes.Property && (memberInfo as PropertyInfo).CanRead);
            Type fieldPropertyType = memberInfo.MemberType == MemberTypes.Field ? (memberInfo as FieldInfo).FieldType : memberInfo.MemberType == MemberTypes.Property ? (memberInfo as PropertyInfo).PropertyType : null;

            if (memberInfo.MemberType == MemberTypes.Method)
            {
                var method = memberInfo as MethodInfo;
                parameters = method.GetParameters();
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i].IsOut)
                    {
                        builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Output_"+ TemplateIO.GetIOName(parameters[i].ParameterType) + " " + parameters[i].Name + ";\n");
                        output.Add(new KeyValuePair <string, Type>(parameters[i].Name, parameters[i].ParameterType));
                    }
                    else
                    {
                        builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Input_"+ TemplateIO.GetIOName(parameters[i].ParameterType) + " " + parameters[i].Name + ";\n");
                        input.Add(new KeyValuePair <string, Type>(parameters[i].Name, parameters[i].ParameterType));
                    }
                }
                methodHasReturn = method.ReturnType != typeof(void);
                if (methodHasReturn)
                {
                    builder.Append("\n");
                    builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Output_"+ TemplateIO.GetIOName(method.ReturnType) + " returnVar;\n");
                    output.Add(new KeyValuePair <string, Type>("returnVar", method.ReturnType));
                }

                if (isSpecialSetMethod)
                {
                    fieldPropertyType = parameters[0].ParameterType;
                }
            }
            else
            {
                if (canRead)
                {
                    builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Output_"+ TemplateIO.GetIOName(fieldPropertyType) + " getter;\n");
                    output.Add(new KeyValuePair <string, Type>("getter", fieldPropertyType));
                }
            }
            if (canWrite)
            {
                builder.Append("		[HideInNormalInspector] [UnityEngine.SerializeField] public Input_"+ TemplateIO.GetIOName(fieldPropertyType) + " setter;\n");
                input.Add(new KeyValuePair <string, Type>("setter", fieldPropertyType));
            }

            builder.Append("\n");
            builder.Append("\n");



            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("		public override void Init() {\n");
            var nodeName = memberInfo.Name;

            if (memberInfo.MemberType == MemberTypes.Method && ((MethodInfo)memberInfo).IsSpecialName)
            {
                var name = memberInfo.Name;
                if (name.StartsWith("get_"))
                {
                    nodeName = name.Substring("get_".Length);
                }
                else if (name.StartsWith("op_"))
                {
                    nodeName = OperatorFromName[name];
                }
            }
            builder.Append("			SetName(\""+ nodeName + "\");\n");
            if (useReference)
            {
                builder.Append("			refObject = "+ CreateIOConstructorParam(memberInfo.DeclaringType, "Input") + ";\n");
                if (memberInfo.DeclaringType.IsValueType)
                {
                    builder.Append("			newRefObject = "+ CreateIOConstructorParam(memberInfo.DeclaringType, "Output") + ";\n");
                }
            }
            foreach (var i in input)
            {
                builder.Append("			"+ i.Key + " = " + CreateIOConstructorParam(i.Value, "Input") + ";\n");
            }
            foreach (var i in output)
            {
                builder.Append("			"+ i.Key + " = " + CreateIOConstructorParam(i.Value, "Output") + ";\n");
            }
            builder.Append("		}\n");
            builder.Append("#endif\n");

            builder.Append("\n");

            builder.Append("		public override NodeTreeOutput Tick(out ExecutionResult executionResult, ExecutionResult childResult) {\n");
            if (memberInfo.MemberType == MemberTypes.Method && !isSpecialSetMethod)
            {
                builder.Append("			Calculate();\n");
            }
            else if (canWrite)
            {
                if (useReference)
                {
                    builder.Append("			(("+ memberInfo.DeclaringType.FullName + ") refObject.GetValue()).");
                }
                else
                {
                    builder.Append(memberInfo.DeclaringType.FullName + ".");
                }
                if (isSpecialSetMethod)
                {
                    builder.Append(memberInfo.Name.Substring("set_".Length));
                }
                else
                {
                    builder.Append(memberInfo.Name);
                }
                builder.Append(" = setter.GetValue();\n");
            }

            builder.Append("			executionResult = ExecutionResult.Success;\n");
            builder.Append("			return null;\n");
            builder.Append("		}\n");

            builder.Append("\n");

            builder.Append("		public override void Calculate() {");
            if (canRead)
            {
                builder.Append("\n");
                builder.Append("				getter.value = (");
                if (useReference)
                {
                    builder.Append("((" + memberInfo.DeclaringType.FullName + ") refObject.GetValue()).");
                }
                else
                {
                    builder.Append(memberInfo.DeclaringType.FullName + ".");
                }
                builder.Append(memberInfo.Name + ");\n");
            }
            else if (!isSpecialSetMethod)
            {
                builder.Append("\n");
                builder.Append("			");
                WriteMethodAction(memberInfo, parameters, builder, methodHasReturn, useReference);
            }
            builder.Append("		}\n");

            builder.Append("\n");
            builder.Append("#if UNITY_EDITOR\n");
            builder.Append("		protected override void Draw() {\n");
            if (useReference)
            {
                builder.Append("			refObject.DisplayLayout(\"Reference\");\n");
                if (memberInfo.DeclaringType.IsValueType)
                {
                    builder.Append("			newRefObject.DisplayLayout(\"Reference\");\n");
                }
            }

            if (memberInfo.MemberType == MemberTypes.Method)
            {
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (!parameters[i].IsOut)
                    {
                        builder.Append("			"+ parameters[i].Name + ".DisplayLayout(\"" + parameters[i].Name + "\");\n");
                    }
                }
                for (int i = 0; i < parameters.Length; i++)
                {
                    if (parameters[i].IsOut)
                    {
                        builder.Append("			"+ parameters[i].Name + ".DisplayLayout(\"" + parameters[i].Name + "\");\n");
                    }
                }
                if (methodHasReturn)
                {
                    builder.Append("			returnVar.DisplayLayout(\"Return\");\n");
                }
            }
            else
            {
                builder.Append("			UnityEngine.GUILayout.BeginHorizontal();\n");
                if (canWrite)
                {
                    builder.Append("			setter.DisplayLayout(\"set "+ memberInfo.Name + "\");\n");
                }
                if (canRead)
                {
                    builder.Append("			getter.DisplayLayout(\"get "+ memberInfo.Name + "\");\n");
                }
                builder.Append("			UnityEngine.GUILayout.EndHorizontal();\n");
            }

            builder.Append("		}\n");
            builder.Append("#endif\n");
            builder.Append("	}\n");
            builder.Append("}\n");

            return(builder.ToString());
        }
 /// <param name="ioType">Input or Output</param>
 static string CreateIOConstructorParam(Type type, string ioType)
 {
     return(type.IsValueType || type.IsAbstract || type.GetConstructors().Count(c => c.GetParameters().Length == 0) == 0 ?
            "CreateIO<" + ioType + "_" + TemplateIO.GetIOName(type) + ">()" :
            "Create" + ioType + "<" + ioType + "_" + TemplateIO.GetIOName(type) + ", " + type.FullName + ">(new " + type.FullName + "())");
 }
        // Do something similar for structs and enums like Vector3
        void OnGUI()
        {
            var toggleStyle = EditorStyles.toggle;

            toggleStyle.richText = true;
            var buttonStyle = GUI.skin.button;

            buttonStyle.richText  = true;
            buttonStyle.alignment = TextAnchor.MiddleLeft;



            if (CheckStep("Step 1: Select Nodes to create", 1))
            {
                string searchLower = search.ToLower();

                int matches = TypeFinder.Types.Where(t => search == "" || t.FullName.ToLower().Contains(searchLower)).Count();

                search      = EditorGUILayout.TextField("Search (Matches: " + matches + ")", search);
                searchLower = search.ToLower();

                scroll = GUILayout.BeginScrollView(scroll, false, true);
                foreach (var type in TypeFinder.Types)
                {
                    if (search == "" || type.FullName.ToLower().Contains(searchLower))
                    {
                        if (GUILayoutCull.Button(position, scroll, type.FullName, buttonStyle))
                        {
                            clickedType = clickedType == type ? null : type;

                            if (clickedType == type)
                            {
                                currentTypeMethods             = TypeFinder.GetMethods(type);
                                currentTypePropertiesAndFields = TypeFinder.GetPropertiesAndFields(type);
                            }
                        }
                        if (clickedType == type)
                        {
                            GUILayout.Label("Struct/class", EditorStyles.boldLabel);
                            bool inside = WantedStructs.Contains(type);
                            if (inside != GUILayout.Toggle(inside, type.Name, toggleStyle))
                            {
                                if (inside)
                                {
                                    WantedStructs.Remove(type);
                                }
                                else
                                {
                                    WantedStructs.Add(type);
                                }
                            }

                            /*
                             * GUILayout.Label("Constructors", EditorStyles.boldLabel);
                             * // TO DO
                             */
                            GUILayout.Label("Methods", EditorStyles.boldLabel);
                            foreach (var elem in currentTypeMethods)
                            {
                                inside = WantedMembers.Contains(elem);
                                if (inside != GUILayout.Toggle(inside, TypeFinder.GetDisplayName(elem, true, true), toggleStyle))
                                {
                                    if (inside)
                                    {
                                        WantedMembers.Remove(elem);
                                    }
                                    else
                                    {
                                        WantedMembers.Add(elem);
                                    }
                                }
                            }

                            GUILayout.Label("Properties and Fields", EditorStyles.boldLabel);
                            foreach (var elem in currentTypePropertiesAndFields)
                            {
                                inside = WantedMembers.Contains(elem);
                                if (inside != GUILayout.Toggle(inside, TypeFinder.GetDisplayName(elem, true, true), toggleStyle))
                                {
                                    if (inside)
                                    {
                                        WantedMembers.Remove(elem);
                                    }
                                    else
                                    {
                                        WantedMembers.Add(elem);
                                    }
                                }
                            }
                        }
                    }
                }
                GUILayout.EndScrollView();
            }
            EditorGUILayout.EndToggleGroup();

            if (CheckStep("Step 2: Generate files", 2))
            {
                scroll = GUILayout.BeginScrollView(scroll, false, true);
                GUILayout.Label("Structs", EditorStyles.boldLabel);
                foreach (var elem in WantedStructs)
                {
                    if (!GUILayout.Toggle(true, elem.FullName + " - " + elem.FullName, toggleStyle))
                    {
                        WantedStructs.Remove(elem);
                        break;
                    }
                }

                GUILayout.Label("Methods, Properties and Fields", EditorStyles.boldLabel);
                foreach (var elem in WantedMembers)
                {
                    if (!GUILayout.Toggle(true, elem.DeclaringType.FullName + " - " + TypeFinder.GetDisplayName(elem, true, true), toggleStyle))
                    {
                        WantedMembers.Remove(elem);
                        break;
                    }
                }
                GUILayout.EndScrollView();

                if (GUILayout.Button("Generate Files"))
                {
                    var fullPath = GetFullPath();

                    foreach (var elem in WantedMembers)
                    {
                        string filename;
                        string textFile = TemplateNode.GenerateFile(elem, out filename);
                        WriteFile(fullPath, "Nodes", filename, textFile);
                    }

                    foreach (var elem in WantedStructs)
                    {
                        string filename;
                        string textFile = TemplateIO.GenerateFileInput(elem, out filename);
                        WriteFile(fullPath, "IO", filename, textFile);
                        textFile = TemplateIO.GenerateFileOutput(elem, out filename);
                        WriteFile(fullPath, "IO", filename, textFile);
                        textFile = TemplateIO.GenerateValueFile(elem, out filename);
                        WriteFile(fullPath, "Values", filename, textFile);
                    }

                    AssetDatabase.Refresh();
                }
            }
            EditorGUILayout.EndToggleGroup();
        }