/* Raylib-cs
 * Program.cs - Generator for creating pinvoke bindings from raylib headers.
 * Copyright 2019
 *
 * Release under zLib License.
 * See LICENSE for details.
 */

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Globalization;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis;
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;

namespace Generator
{
    public struct TypeMember
    {
        public string Name;
        public Type Type;
        public int Offset;

        public TypeMember(string name, Type type, int offset)
        {
            Name = name;
            Type = type;
            Offset = offset;
        }
    }

    public struct UserDefinedEnumData
    {
        public string Name;
        public List<KeyValuePair<string, int>> Enums;

        public UserDefinedEnumData(string name)
        {
            Name = name;
            Enums = new List<KeyValuePair<string, int>>();
        }
    }

    public struct UserDefinedTypeData
    {
        public string Name;
        public List<TypeMember> Members;

        public UserDefinedTypeData(string name)
        {
            Name = name;
            Members = new List<TypeMember>();
        }
    }

    public struct Function
    {
        public string Name;
        public Type ReturnType;
        public List<FunctionParam> Params;

        public Function(string name, string returnType, params FunctionParam[] parameters)
        {
            var Isreturnpointer = name[0] == '*';
            Name = Isreturnpointer ? name.Replace("*", "") : name;
            ReturnType = new Type(returnType, Isreturnpointer);
            Params = new List<FunctionParam>(parameters);
        }

        public override string ToString()
        {
            var str = $"{ReturnType} {Name}(";
            for (int i = 0; i < Params.Count; i++)
            {
                if (i != 0) str += ",";
                str += $"{Params[i].ToString()}";
            }
            str += ")";
            return str;
        }
    }

    public struct Type
    {
        public string Name;
        public bool IsPointer;

        public Type(string name, bool isPointer)
        {
            Name = name;
            IsPointer = isPointer;
        }

        public override string ToString()
        {
            return Name + (IsPointer == true ? "*" : "");
        }
    }

    public struct FunctionParam
    {
        public string Name;
        public Type Type;

        public FunctionParam(string name, Type type)
        {
            Name = name;
            Type = type;
        }

        public FunctionParam(string FullParam)
        {
            if (FullParam == "")
            {
                Name = "";
                Type = new Type();
                return;
            }

            FullParam = FullParam.Trim();
            if (FullParam.Split(' ').Length == 2)
            {
                var Isreturnpointer = FullParam.Split(' ')[1][0] == '*';
                Name = Isreturnpointer ? FullParam.Replace("*", "").Split(' ')[1] : FullParam.Split(' ')[1];
                Type = new Type(FullParam.Replace("*", "").Split(' ')[0], Isreturnpointer);
            }
            else
            {
                var Isreturnpointer = FullParam.Split(' ')[2][0] == '*';
                Name = Isreturnpointer ? FullParam.Replace("*", "").Split(' ')[2] : FullParam.Split(' ')[2];
                Type = new Type(FullParam.Replace("*", "").Split(' ')[0] + " " + FullParam.Replace("*", "").Split(' ')[1], Isreturnpointer);
            }
        }

        public override string ToString()
        {
            return Name == "..." ? "..." : $"{Type.ToString()} {Name}";
        }
    }

    struct SyntaxData
    {
        public string tag;
        public List<string> functions;
        public HashSet<string> types;
        public HashSet<string> enums;
        public List<Function> funcs;
        public List<string> comments;
        public Dictionary<string, UserDefinedTypeData> tps;
        public Dictionary<string, UserDefinedEnumData> eps;

        public SyntaxData(string _tag)
        {
            tag = _tag;
            functions = new List<string>();
            types = new HashSet<string>();
            enums = new HashSet<string>();
            funcs = new List<Function>();
            comments = new List<string>();
            tps = new Dictionary<string, UserDefinedTypeData>();
            eps = new Dictionary<string, UserDefinedEnumData>();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            var process = Process.Start(new ProcessStartInfo("Dia2Dump.exe", " -t raylib.pdb") { RedirectStandardOutput = true, UseShellExecute = false });
            
            string typedata = "";
            var ischkout = process.StandardOutput;
            
            while (process.HasExited == false)
            {
                typedata += ischkout.ReadToEnd();
            }
            File.WriteAllText("types.txt", typedata);
            var typesfile = File.ReadAllLines("types.txt");

            var TypeMap = new Dictionary<string, string>();
            typesfile = typesfile.Where(x => x != "").ToArray();

            Console.WriteLine("Enter the path to raylib.");
            var raylibPath = Console.ReadLine() + "/src/";

            var sources = new KeyValuePair<string, string>[] {
                new KeyValuePair<string, string>("raylib.h","RLAPI"),
                new KeyValuePair<string, string>("raymath.h", "RMDEF"),
                new KeyValuePair<string, string>("physac.h", "PHYSACDEF"),
                new KeyValuePair<string, string>( "easings.h","EASEDEF"),
                new KeyValuePair<string, string>( "raygui.h","RAYGUIDEF")
            };

            foreach (var sourceAndExportTag in sources)
            {
                var sourcefilename = sourceAndExportTag.Key;
                var FileName = new CultureInfo("en-us", false).TextInfo.ToTitleCase(sourcefilename.Replace(".h", ""));
                var ExportTag = sourceAndExportTag.Value;
                var sourcefile = File.ReadAllLines(raylibPath + sourcefilename);
            
                var syntax = GetSyntax(sourcefile, ExportTag, TypeMap, typesfile);
                GenerateBinding(syntax, FileName);
            }
        }

        // Takes the source from a raylib module and stores the syntax data
        static SyntaxData GetSyntax(string[] sourcefile, string tag, Dictionary<string, string> TypeMap, string[] typesfile)
        {
            var syntax = new SyntaxData(tag);

            // local references
            var ExportTag = syntax.tag;
            var comments = syntax.comments;
            var functions = syntax.functions;
            var enums = syntax.enums;
            var Funcs = syntax.funcs;
            var types = syntax.types;
            var tps = syntax.tps;
            var eps = syntax.eps;

            for (int i = 0; i < sourcefile.Length; i++)
            {
                var source = sourcefile[i].Split("//".ToCharArray())[0].Trim();

                // Comments test
                if (source.StartsWith("*") || source.StartsWith("//"))
                {
                    comments.Add(source);
                }

                if (source.Contains(ExportTag))
                {
                    if (!source.Contains("#define"))
                    {
                        source = source.TrimStart(ExportTag.ToCharArray()).Trim();
                        if (!source.Contains("{")) 
                        {
                            // Some functions go across multiple lines
                            if (source[source.Length - 1] == ',')
                            {
                                i++;
                                var nextLine = sourcefile[i].Split("//".ToCharArray())[0].Trim();
                                source += " " + nextLine;
                            }

                            functions.Add(source);
                        }
                    }
                }

                if (source.Contains("typedef"))
                {
                    if (source.Split(' ')[1].Trim() == "struct")
                    {
                        if (source.Contains('}'))
                        {
                            types.Add(source.Split('}', ';')[1].Trim());
                        }
                        else
                        {
                            int t = 1;
                            var src = sourcefile[i + t];
                            while (!src.Contains('}'))
                            {
                                src = sourcefile[i + t];
                                t++;
                            }
                            types.Add(src.Trim('}', ';').Trim());
                        }
                    }
                }
            }

            // Break down function string
            for (int i = 0; i < functions.Count; i++)
            {
                var func = functions[i];
                var returntype = func.Split('(')[0].Split(' ').Length == 3 ? func.Split('(')[0].Split(' ')[0] + " " + func.Split('(')[0].Split(' ')[1] : func.Split('(')[0].Split(' ')[0];

                types.Add(returntype);
                func = func.Substring(returntype.Length).Trim();

                //@TODO extra comma in param list
                var funcname = func.Split('(')[0].Trim();
                var Func = new Function(funcname, returntype);
                if (func.Contains(','))
                {
                    var Params = func.Split('(', ')')[1].Split(',');
                    for (int t = 0; t < Params.Length; t++)
                    {
                        var Param = Params[t];
                        Param = Param.Trim();
                        if (Param == "...")
                        {
                            Func.Params.Add(new FunctionParam(Param, new Type("...", false)));
                        }
                        else
                        {
                            var TypeAndVar = Param.Trim().Split(' ');
                            if (TypeAndVar.Length == 2)
                            {
                                var type = TypeAndVar[0];
                                var Var = TypeAndVar[1];
                                types.Add(type);
                            }
                            else if (TypeAndVar.Length == 3)
                            {
                                var type = TypeAndVar[0] + " " + TypeAndVar[1];
                                var Var = TypeAndVar[2];
                                types.Add(type);
                            }

                            Func.Params.Add(new FunctionParam(Param));
                        }
                    }
                }
                else if (func.Contains(' '))
                {
                    var Param = func.Split('(', ')')[1];
                    var TypeAndVar = Param.Trim().Split(' ');
                    var type = TypeAndVar[0];
                    var Var = TypeAndVar[1];

                    types.Add(type);
                    Func.Params.Add(new FunctionParam(Param));
                }
                Funcs.Add(Func);
            }

            for (int i = 0; i < sourcefile.Length; i++)
            {
                var source = sourcefile[i].Split("//".ToCharArray())[0].Trim();
                if (source.Contains("typedef"))
                {
                    if (source.Split(' ')[1].Trim() != "struct")
                    {
                        if (source.Split(' ')[1].Trim() == "enum")
                        {
                            if (source.Contains('}'))
                            {
                                enums.Add(source.Split('}', ';')[1].Trim());
                            }
                            else
                            {
                                int t = 1;
                                var src = sourcefile[i + t];
                                while (!src.Contains('}'))
                                {
                                    src = sourcefile[i + t];
                                    t++;
                                }
                                enums.Add(src.Trim('}', ';').Trim());
                            }
                        }
                        else
                        {
                            TypeMap.Add(source.Split(" ".ToCharArray(), 3)[2].Trim(';').Trim(), source.Split(" ".ToCharArray(), 3)[1].Trim());
                        }
                    }
                    else
                    {

                    }
                }
                else if (source.Contains("#define") && source.Split(' ').Length > 2 && source.Split(' ')[1] != (ExportTag))
                {
                    if (types.Contains(source.Split(" ".ToCharArray(), 3)[1].Trim(';').Trim())) TypeMap.Add(source.Split(" ".ToCharArray(), 3)[1].Trim(';').Trim(), source.Split(" ".ToCharArray(), 3)[2].Trim());
                }
            }

            for (int i = 0; i < Funcs.Count; i++)
            {
                var Func = Funcs[i];
                if (TypeMap.ContainsKey(Func.ReturnType.Name))
                {
                    Func.ReturnType.Name = TypeMap[Func.ReturnType.Name];
                }

                for (int t = 0; t < Func.Params.Count; t++)
                {
                    if (TypeMap.ContainsKey(Func.Params[t].Type.Name))
                    {

                        Func.Params[t] = new FunctionParam(Func.Params[t].Name, new Type(TypeMap[Func.Params[t].Type.Name], Func.Params[t].Type.IsPointer));
                    }
                }
                Funcs[i] = Func;
            }

            for (int i = 0; i < typesfile.Length; i++)
            {
                var typestr = typesfile[i];
                if (typestr.Contains("UserDefinedType: ") && !typestr.Contains("UserDefinedType:  "))
                {
                    var type = typestr.Substring("UserDefinedType: ".Length).Trim();
                    if (types.Contains(type) && type[0] != ' ' && !tps.Any(x => x.Key == type))
                    {
                        var usertype = new UserDefinedTypeData(type);
                        int t = 1;
                        typestr = typesfile[i + t];

                        while (!(typestr.Contains("UserDefinedType: ") && typestr["UserDefinedType: ".Length - 1] == ' '))
                        {
                            if (typestr.Contains("Member"))
                            {
                                var tpsrc = typesfile[i + t];
                                var MemberNameandType = tpsrc.Substring(tpsrc.IndexOf("Type:") + "Type:".Length).Trim().Split(',');
                                var MemberName = MemberNameandType[1].Trim();
                                var MemberType = MemberNameandType[0].Trim();

                                if (MemberType.Contains("<unnamed-enum-false>"))
                                {
                                    MemberType = "bool";
                                }

                                var isptr = MemberType[MemberType.Length - 1] == '*';
                                var offset = int.Parse(tpsrc.Split(',')[0].Split(':')[1].Trim().Substring("this+0x".Length), System.Globalization.NumberStyles.HexNumber);

                                if (isptr)
                                {
                                    MemberType = MemberType.Remove(MemberType.Length - 1);
                                }
                                usertype.Members.Add(new TypeMember(MemberName, new Type(MemberType, isptr), offset));

                                if (typesfile[i + t + 1].Contains(MemberType.Trim("struct".ToCharArray())) &&
                                    typesfile[i + t + 1].Contains("UserDefinedType:  "))
                                {
                                    t++;
                                }
                            }
                            t++;
                            typestr = typesfile[i + t];
                        }
                        tps.Add(usertype.Name, usertype);
                    }
                }

                if (typestr.Contains("Enum           : "))
                {
                    var Enum = typestr.Substring("Enum           : ".Length).Split(',')[0].Trim();
                    if (enums.Contains(Enum) && Enum != "bool")
                    {
                        var UserDefineEnum = new UserDefinedEnumData(Enum);
                        int t = 1;
                        typestr = typesfile[i + t];

                        while (typestr.Contains("Constant"))
                        {
                            var intstr = typestr.Split(",".ToCharArray(), 2)[0].Split(':')[1].Split(' ')[4].Substring("0x".Length).Trim();
                            int Int = int.Parse(intstr, System.Globalization.NumberStyles.HexNumber);
                            var name = typestr.Split(",".ToCharArray(), 4)[3].Trim();
                            UserDefineEnum.Enums.Add(new KeyValuePair<string, int>(name, Int));
                            t++;
                            typestr = typesfile[i + t];
                        }
                        eps.Add(Enum, UserDefineEnum);
                    }
                }
            }
            types.IntersectWith(tps.Select(x => x.Key));

            return syntax;
        }

        // Takes in SyntaxData and creates a .cs binding file
        static void GenerateBinding(SyntaxData syntax, string fileName)
        {
            // local references
            var ExportTag = syntax.tag;
            var comments = syntax.comments;
            var functions = syntax.functions;
            var enums = syntax.enums;
            var Funcs = syntax.funcs;
            var types = syntax.types;
            var tps = syntax.tps;
            var eps = syntax.eps;

            // ----------------------------------------------------------------------------
            // Begin generating bindings here using SyntaxTree
            // ----------------------------------------------------------------------------
            var codetree = CompilationUnit().AddUsings(UsingDirective(ParseName("System")))
                .AddUsings(UsingDirective(ParseName("System.IO")))
                .AddUsings(UsingDirective(ParseName("System.Collections.Generic")))
                .AddUsings(UsingDirective(ParseName("System.Security")))
                .AddUsings(UsingDirective(ParseName("System.Runtime.InteropServices")));

            var Raylibnamespace = NamespaceDeclaration(ParseName("Raylib")).NormalizeWhitespace();

            foreach (var Enumtype in eps.Values)
            {
                var Enum = EnumDeclaration(Enumtype.Name).WithModifiers(TokenList(Token(SyntaxKind.PublicKeyword)));
                foreach (var Member in Enumtype.Enums)
                {
                    var enummember = EnumMemberDeclaration(
                            Identifier(Member.Key))
                        .WithEqualsValue(
                            EqualsValueClause(
                                LiteralExpression(
                                    SyntaxKind.NumericLiteralExpression,
                                    Literal(Member.Value))));
                    Enum = Enum.AddMembers(enummember);
                }
                Raylibnamespace = Raylibnamespace.AddMembers(Enum);
            }

            foreach (var Type in tps.Values)
            {
                var Struct = StructDeclaration(Type.Name).WithAttributeLists(
                    SingletonList(
                        AttributeList(
                            SingletonSeparatedList(
                                Attribute(
                                    IdentifierName("StructLayout"))
                                .WithArgumentList(
                                    AttributeArgumentList(
                                        SeparatedList<AttributeArgumentSyntax>(
                                            new SyntaxNodeOrToken[]{
                                            AttributeArgument(
                                                MemberAccessExpression(
                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                    IdentifierName("LayoutKind"),
                                                    IdentifierName("Sequential"))),
                                            Token(SyntaxKind.CommaToken),
                                            AttributeArgument(
                                                MemberAccessExpression(
                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                    IdentifierName("CharSet"),
                                                    IdentifierName("Ansi")))
                                            .WithNameEquals(
                                                NameEquals(
                                                    IdentifierName("CharSet")))})))))))
                    .WithModifiers(
                        TokenList(
                            Token(SyntaxKind.PublicKeyword)));


                bool IsUnsafe = false;
                var FixedStructTypes = new List<string>();

                foreach (var Member in Type.Members)
                {
                    var IsStruct = false;
                    var TypeName = (Member.Type.IsPointer ? "IntPtr" : Member.Type.Name).Trim();
                    if (TypeName.Contains("struct"))
                    {
                        IsStruct = true;
                        TypeName = TypeName.Substring(TypeName.LastIndexOf("struct") + "struct".Length);
                    }

                    if (TypeName.Contains("enum"))
                    {
                        TypeName = TypeName.Substring(TypeName.LastIndexOf("enum") + "enum".Length);
                    }

                    if (TypeName.Contains("unsigned int"))
                    {
                        TypeName = TypeName.Replace("unsigned int", "uint");
                    }

                    if (TypeName.Contains("unsigned char"))
                    {
                        TypeName = TypeName.Replace("unsigned char", "byte");
                    }

                    var IsFixed = false;
                    var VariableDec = VariableDeclarator(Member.Name);
                    if (TypeName.Contains("["))
                    {
                        int arraycount = int.Parse(TypeName.Split('[', ']')[1].Substring("0x".Length), System.Globalization.NumberStyles.HexNumber);
                        TypeName = TypeName.Split('[', ']')[0].Trim();
                        if (IsStruct)
                        {
                            if (!FixedStructTypes.Contains($"_{TypeName}_e_FixedBuffer_{arraycount}"))
                            {
                                FixedStructTypes.Add($"_{TypeName}_e_FixedBuffer_{arraycount}");
                                var FixedBufferStruct = StructDeclaration($"_{TypeName}_e_FixedBuffer_{arraycount}")
                                    .WithModifiers(
                                        TokenList(
                                            new[]{
                                        Token(SyntaxKind.PublicKeyword),
                                        Token(SyntaxKind.UnsafeKeyword)}));

                                for (int i = 0; i < arraycount; i++)
                                {
                                    FixedBufferStruct = FixedBufferStruct.AddMembers(
                        FieldDeclaration(
                            VariableDeclaration(
                                IdentifierName(TypeName))
                            .WithVariables(
                                SingletonSeparatedList<VariableDeclaratorSyntax>(
                                    VariableDeclarator(
                                        Identifier($"{Member.Name}{i}")))))
                        .WithModifiers(
                            TokenList(
                                Token(SyntaxKind.PublicKeyword))));
                                }


                                FixedBufferStruct = FixedBufferStruct.AddMembers(IndexerDeclaration(
                        RefType(
                            IdentifierName(TypeName)))
                    .WithModifiers(
                        TokenList(
                            Token(SyntaxKind.PublicKeyword)))
                    .WithParameterList(
                        BracketedParameterList(
                            SingletonSeparatedList<ParameterSyntax>(
                                Parameter(
                                    Identifier("index"))
                                .WithType(
                                    PredefinedType(
                                        Token(SyntaxKind.IntKeyword))))))
                    .WithAccessorList(
                        AccessorList(
                            SingletonList<AccessorDeclarationSyntax>(
                                AccessorDeclaration(
                                    SyntaxKind.GetAccessorDeclaration)
                                .WithBody(
                                    Block(
                                        SingletonList<StatementSyntax>(
                                            FixedStatement(
                                                VariableDeclaration(
                                                    PointerType(
                                                        IdentifierName(TypeName)))
                                                .WithVariables(
                                                    SingletonSeparatedList<VariableDeclaratorSyntax>(
                                                        VariableDeclarator(
                                                            Identifier("e"))
                                                        .WithInitializer(
                                                            EqualsValueClause(
                                                                PrefixUnaryExpression(
                                                                    SyntaxKind.AddressOfExpression,
                                                                    IdentifierName($"{Member.Name}0")))))),
                                                ReturnStatement(
                                                    RefExpression(
                                                        ElementAccessExpression(
                                                            IdentifierName("e"))
                                                        .WithArgumentList(
                                                            BracketedArgumentList(
                                                                SingletonSeparatedList<ArgumentSyntax>(
                                                                    Argument(
                                                                        IdentifierName("index")))))))))))))));
                                Raylibnamespace = Raylibnamespace.AddMembers(FixedBufferStruct);
                            }
                            TypeName = $"_{TypeName}_e_FixedBuffer_{arraycount}";
                        }
                        else
                        {
                            VariableDec = VariableDec.WithArgumentList(
                                    BracketedArgumentList(
                                        SingletonSeparatedList<ArgumentSyntax>(
                                            Argument(
                                                LiteralExpression(
                                                    SyntaxKind.NumericLiteralExpression,
                                                    Literal(arraycount))))));
                            IsFixed = true;
                            IsUnsafe = true;
                        }
                    }

                    var variable = VariableDeclaration(ParseTypeName(TypeName)).AddVariables(VariableDec);
                    var field = FieldDeclaration(variable).AddModifiers(Token(SyntaxKind.PublicKeyword));
                    if (IsFixed) field = field.AddModifiers(Token(SyntaxKind.FixedKeyword));
                    Struct = Struct.AddMembers(field);
                }

                if (IsUnsafe) Struct = Struct.AddModifiers(Token(SyntaxKind.UnsafeKeyword));
                Raylibnamespace = Raylibnamespace.AddMembers(Struct);
            }

            {
                var RaylibClass = ClassDeclaration(fileName)
        .WithAttributeLists(
            SingletonList<AttributeListSyntax>(
                AttributeList(
                    SingletonSeparatedList<AttributeSyntax>(
                        Attribute(
                            IdentifierName("SuppressUnmanagedCodeSecurity"))))))
        .WithModifiers(
            TokenList(
                new[]{
                Token(SyntaxKind.PublicKeyword),
                Token(SyntaxKind.StaticKeyword),
                Token(SyntaxKind.PartialKeyword)}));

                var LibraryNameField = FieldDeclaration(
                    VariableDeclaration(
                        PredefinedType(
                            Token(SyntaxKind.StringKeyword)))
                    .WithVariables(
                        SingletonSeparatedList<VariableDeclaratorSyntax>(
                            VariableDeclarator(
                                Identifier("nativeLibName"))
                            .WithInitializer(
                                EqualsValueClause(
                                    LiteralExpression(
                                        SyntaxKind.StringLiteralExpression,
                                        Literal("raylib")))))))
                .WithModifiers(
                    TokenList(
                        new[]{
                        Token(SyntaxKind.PublicKeyword),
                        Token(SyntaxKind.ConstKeyword)}));
                
                RaylibClass = RaylibClass.AddMembers(LibraryNameField);
                foreach (var Func in Funcs)
                {
                    var typename = Func.ReturnType.IsPointer ? "IntPtr" : Func.ReturnType.Name;
                    var Function = MethodDeclaration(ParseTypeName(typename),
                    Identifier(Func.Name))
                .WithAttributeLists(
                    SingletonList(
                        AttributeList(
                            SingletonSeparatedList(
                                Attribute(
                                    IdentifierName("DllImport"))
                                .WithArgumentList(
                                    AttributeArgumentList(
                                        SeparatedList<AttributeArgumentSyntax>(
                                            new SyntaxNodeOrToken[]{
                                            AttributeArgument(
                                                IdentifierName("nativeLibName")),
                                            Token(SyntaxKind.CommaToken),
                                            AttributeArgument(
                                                MemberAccessExpression(
                                                    SyntaxKind.SimpleMemberAccessExpression,
                                                    IdentifierName("CallingConvention"),
                                                    IdentifierName("Cdecl")))
                                            .WithNameEquals(
                                                NameEquals(
                                                    IdentifierName("CallingConvention")))})))))))
                .WithModifiers(
                    TokenList(
                        new[]{
                        Token(SyntaxKind.PublicKeyword),
                        Token(SyntaxKind.StaticKeyword),
                        Token(SyntaxKind.ExternKeyword)
                        })).WithSemicolonToken(Token(SyntaxKind.SemicolonToken));

                    for (int i = 0; i < Func.Params.Count; i++)
                    {
                        var Param = Func.Params[i];
                        var TypeName = (Param.Type.IsPointer ? "IntPtr" : Param.Type.Name).Trim();
                        if (TypeName.Contains("unsigned int"))
                        {
                            TypeName = TypeName.Replace("unsigned int", "uint");
                        }

                        if (TypeName.Contains("..."))
                        {
                            TypeName = "params object[]";
                            Param.Name = "args";
                        }

                        if (TypeName.Contains("unsigned char"))
                        {
                            TypeName = TypeName.Replace("unsigned char", "byte");
                        }

                        if (TypeName.Contains("struct"))
                        {
                            TypeName = TypeName.Substring(TypeName.LastIndexOf("struct") + "struct".Length);
                        }
                        Function = Function.AddParameterListParameters(
                        Parameter(
                            Identifier(Param.Name))
                        .WithType(
                            IdentifierName(TypeName)));
                    }

                    RaylibClass = RaylibClass.AddMembers(Function);
                }
                Raylibnamespace = Raylibnamespace.AddMembers(RaylibClass);
            }         
            codetree = codetree.AddMembers(Raylibnamespace);
            Console.WriteLine(codetree.NormalizeWhitespace().ToFullString());
            File.WriteAllText($"{fileName}.cs", codetree.NormalizeWhitespace().ToFullString());
   
            Console.WriteLine("Finished generating bindings for file ");
            Console.ReadLine();

            /* Raylib-cs
            * Raylib.cs - Core bindings to raylib
            * Copyright 2019 Chris Dill
            *
            * Release under zLib License.
            * See LICENSE for details.
            */
        }
    }
}