/// <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 + "())");
 }
        // 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());
        }