private void TranslateEnumGroups(XmlSpecData spec, DotNetApiData api, Options options)
        {
            foreach (var specEnumGroup in spec.EnumGroups.Where(x => options.EnumGroupFilter(x.Name)))
            {
                var enumGroupData = new DotNetEnumGroupData()
                {
                    Name = specEnumGroup.Name,
                };

                foreach (var enumName in specEnumGroup.Enums.Distinct())
                {
                    var xmlEnumData = spec.Enums.SingleOrDefault(x => x.Name == enumName);

                    if (xmlEnumData == null || !options.EnumFilter(xmlEnumData))
                        continue;

                    var enumData = api.Enums.SingleOrDefault(x => x.OriginalName == enumName);

                    if (enumData == null)
                        continue;

                    enumGroupData.Enums.Add(enumData);

                    if (spec.Enums.Single(x => x.Name == enumName).Type == "bitmask")
                        enumGroupData.IsFlags = true;
                }

                api.EnumGroups.Add(enumGroupData);
            }
        }
		private void WriteConstantsFile(DotNetApiData api, Options options)
		{
			string fileName = Path.Combine(options.OutputPath, options.ConstantsFileName);
			using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
			{
				file.WriteLine("using System;");
				file.WriteLine();
				file.WriteLine($"namespace {options.Namespace}");
				file.WriteLine("{");

                file.WriteLine($"\tpublic partial class {options.ConstantsClassName}");
                file.WriteLine("\t{");

				foreach (var @enum in api.Enums)
				{
					file.WriteLine("\t\t/// <summary>");
					file.WriteLine("\t\t/// {0}", @enum.OriginalName);
					file.WriteLine("\t\t/// </summary>");

                    string name = @enum.OriginalName.Substring(options.Prefix.Length + 1);

                    if (char.IsDigit(name[0]))
                        name = "_" + name;

                    file.WriteLine("\t\tpublic const {0} {1} = {2};", @enum.Type, name, @enum.Value);
					file.WriteLine();
				}

                file.WriteLine("\t}");
                file.WriteLine();
				file.WriteLine("}");
			}
		}
 public void WriteFiles(DotNetApiData api, Options options)
 {
     this.WriteConstantsFile(api, options);
     this.WriteEnumFile(api, options);
     this.WriteNativeFunctionsFile(api, options);
     this.WriteDotNetFunctionsFile(api, options);
 }
 private static IEnumerable<DotNetCommandData> GetSpecCommands(DotNetApiData api)
 {
     return api.Commands
         .Where(x => x.IsFromSpec)
         .OrderBy(x => x.VersionMajor)
         .ThenBy(x => x.VersionMinor)
         .ThenBy(x => x.NativeName);
 }
		public void WriteFiles(DotNetApiData api, Options options)
		{
			this.WriteConstantsFile(api, options);
            this.WriteEnumFile(api, options);
            this.WriteCommandsFile(api, options);
			this.WriteContextFile(api, options);
            this.WriteOverloadsFile(api, options);
        }
        public DotNetApiData Translate(XmlSpecData spec, Options options)
        {
            DotNetApiData api = new DotNetApiData();

            this.TranslateEnums(spec, api, options);
            this.TranslateEnumGroups(spec, api, options);
            this.TranslateFunctions(spec, api, options);

            return api;
        }
		private void TranslateEnums(XmlSpecData spec, DotNetApiData api, Options options)
		{			
			foreach (var specEnum in spec.Enums.Where(x => options.EnumFilter(x)))
			{
				var enumData = new DotNetEnumData()
				{
					OriginalName = specEnum.Name,
					Name = this.InflectEnumName(specEnum.Name, options),
					Value = specEnum.Value,
                    Type = specEnum.Value.Length <= 6 ? "uint" : "ulong"
				};

                if (enumData.Value.StartsWith("-"))
                    enumData.Value = string.Format("unchecked((uint){0})", enumData.Value);

				api.Enums.Add(enumData);
			}
		}
        public void WriteNativeFunctionsFile(DotNetApiData api, Options options)
        {
            string fileName = Path.Combine(options.OutputPath, options.FunctionsFileName);
            using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
            {
                file.WriteLine("using System;");
                file.WriteLine("using System.Runtime.InteropServices;");
                file.WriteLine("using System.Text;");
                file.WriteLine();
                file.WriteLine("namespace {0}", options.Namespace);
                file.WriteLine("{");

                file.WriteLine("\tinternal static partial class {0}", options.FunctionsClassName);
                file.WriteLine("\t{");

                file.WriteLine("\t\tprivate const string Library = \"opengl32.dll\";");
                file.WriteLine();

                foreach (var function in api.Functions.Where(x => x.IsNative))
                {
                    if (function.CanPInvoke)
                    {
                        file.WriteLine(this.GetNativeDeclaration("\t\t", function, false));
                    }
                    else
                    {
                        file.WriteLine(this.GetDelegateDeclaration("\t\t", function));
                    }

                    file.WriteLine();
                }

                file.WriteLine("\t}");

                file.WriteLine("}");
            }
        }
        private void TranslateFunctions(XmlSpecData spec, DotNetApiData api, Options options)
        {
            foreach (var specCommand in spec.Commands.Where(x => options.CommandFilter(x)))
            {
                var specFeature = spec.Features.Where(x => x.Commands.Contains(specCommand.Name)).FirstOrDefault();

                var functionData = new DotNetFunctionData()
                {
                    IsNative = true,
                    OriginalName = specCommand.Name,
                    NativeName = this.InflectFunctionNativeName(specCommand.Name, options),
                    DotNetName = this.InflectFunctionDotNetName(specCommand.Name, options),
                    OriginalReturnType = specCommand.ReturnType,
                    NativeReturnType = this.InflectNativeReturnType(specCommand),
                    DotNetReturnType = this.InflectDotNetReturnType(specCommand)
                };

                if (specFeature != null)
                {
                    functionData.VersionMajor = specFeature.VersionMajor;
                    functionData.VersionMinor = specFeature.VersionMinor;
                    functionData.CanPInvoke = specFeature.VersionMajor == 1 && specFeature.VersionMinor <= 1;
                }

                if (functionData.NativeReturnType == "string")
                    functionData.IsUnsafe = true;

                foreach (var specCommandParam in specCommand.Params)
                {
                    var functionParamData = new DotNetFunctionParamData()
                    {
                        OriginalName = specCommandParam.Name,
                        Name = this.InflectFunctionParamName(specCommandParam.Name),
                        OriginalType = specCommandParam.Type,
                        NativeType = this.InflectFunctionParamNativeType(specCommandParam),
                        DotNetType = this.InflectFunctionParamDotNetType(specCommandParam),
                        IsPointer = this.IsTypePointer(specCommandParam.Type),
                        IsOutput = this.IsTypeOutput(specCommandParam.Type),
                        ShouldUseGenerics = this.ShouldUseGenericsForType(specCommandParam.Type),
                        ShouldUseFixed = this.ShouldUseFixedForParam(specCommandParam),
                        ShouldUseAddressOfOperator = this.ShouldUseAddressOfOperatorForParam(specCommandParam)
                    };

                    if (functionParamData.IsPointer)
                        functionData.IsUnsafe = true;

                    if (functionParamData.ShouldUseGenerics)
                        functionData.ShouldUseGenerics = true;

                    if (functionParamData.IsOutput && functionParamData.DotNetType == "IntPtr")
                    {
                        functionParamData.ShouldUseOut = true;
                    }

                    functionData.Params.Add(functionParamData);
                }

                api.Functions.Add(functionData);

                // Create overload which accepts the Enum variant.
                if (specCommand.Params.Any(x => this.IsTypeEnum(x, api)))
                {
                    api.Functions.Add(this.ChangeFunctionParamsToEnums(functionData, specCommand.Params.Where(x => this.IsTypeEnum(x, api))));
                }
            }

            foreach (var functionData in api.Functions.ToArray())
            {
                var specCommand = spec.Commands.Single(x => x.Name == functionData.OriginalName);

                // Commands which take a pointer and it could be a single element.
                if (specCommand.Params.Any(x => this.ShouldChangeFunctionParamToRefOrOut(x)))
                {
                    api.Functions.Add(this.ChangeFunctionParamsToRefOrOut(functionData, specCommand.Params.Where(x => this.ShouldChangeFunctionParamToRefOrOut(x)).Select(x => x.Name)));
                }

                // Commands which take a pointer and it could be a single element.
                if (specCommand.Params.Any(x => this.ShouldChangeFunctionParamToIntPtr(x)))
                {
                    api.Functions.Add(this.ChangeFunctionParamsToIntPtr(functionData, specCommand.Params.Where(x => this.ShouldChangeFunctionParamToIntPtr(x)).Select(x => x.Name)));
                }
            }

            api.Functions.Sort((x, y) => x.OriginalName != null && y.OriginalName != null ? x.OriginalName.CompareTo(y.OriginalName) : 0);
        }
 public bool IsTypeEnum(XmlCommandParamData param, DotNetApiData api)
 {
     return (param.Type == "GLenum" || param.Type == "GLbitfield") &&
            !string.IsNullOrEmpty(param.TypeGroup) &&
            api.EnumGroups.Any(x => x.Name == param.TypeGroup);
 }
        private void WriteEnumFile(DotNetApiData api, Options options)
        {
            if (api.EnumGroups.Count == 0)
                return;

            string fileName = Path.Combine(options.OutputPath, options.EnumsFileName);
            using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
            {
                file.WriteLine("using System;");
                file.WriteLine();
                file.WriteLine("namespace {0}", options.Namespace);
                file.WriteLine("{");

                foreach (var enumGroup in api.EnumGroups)
                {
                    if (enumGroup.IsFlags)
                        file.WriteLine("\t[Flags]");

                    file.WriteLine("\tpublic enum {0} : uint", enumGroup.Name);
                    file.WriteLine("\t{");

                    foreach (var @enum in enumGroup.Enums)
                    {
                        file.WriteLine("\t\t/// <summary>");
                        file.WriteLine("\t\t/// {0}", @enum.OriginalName);
                        file.WriteLine("\t\t/// </summary>");
                        file.WriteLine("\t\t{0} = {1},", @enum.Name, @enum.Value);
                        file.WriteLine();
                    }

                    file.WriteLine("\t}");
                    file.WriteLine();
                }

                file.WriteLine("}");
            }
        }
        public void WriteDotNetFunctionsFile(DotNetApiData api, Options options)
        {
            string fileName = Path.Combine(options.OutputPath, options.ContextFileName);
            using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
            {
                file.WriteLine("using System;");
                file.WriteLine("using System.Runtime.InteropServices;");
                file.WriteLine("using System.Text;");
                file.WriteLine();
                file.WriteLine("namespace {0}", options.Namespace);
                file.WriteLine("{");

                file.WriteLine("\tpublic partial class {0}", options.ContextClassName);
                file.WriteLine("\t{");

                file.WriteLine("\t\tprivate void InitializeDelegates(int versionMajor, int versionMinor)");
                file.WriteLine("\t\t{");

                int versionMajor = -1;
                int versionMinor = -1;

                foreach (var function in api.Functions.Where(x => !x.CanPInvoke && x.IsNative).OrderBy(x => x.VersionMajor).ThenBy(x => x.VersionMinor).ThenBy(x => x.NativeName))
                {
                    if (versionMajor != function.VersionMajor || versionMinor != function.VersionMinor)
                    {
                        if (versionMajor != -1)
                        {
                            file.WriteLine("\t\t\t}");
                            file.WriteLine();
                        }

                        versionMajor = function.VersionMajor;
                        versionMinor = function.VersionMinor;
                        file.WriteLine("\t\t\tif (versionMajor > {0} || (versionMajor == {0} && versionMinor >= {1}))", versionMajor, versionMinor);
                        file.WriteLine("\t\t\t{");
                    }

                    file.WriteLine("\t\t\t\tthis._" + function.NativeName + " = (" + options.FunctionsClassName + "." + function.NativeName + ")this.LoadExtensionFunction<" + options.FunctionsClassName + "." + function.NativeName + ">();");
                }

                file.WriteLine("\t\t\t}");

                file.WriteLine("\t\t}");
                file.WriteLine();

                foreach (var function in api.Functions)
                {
                    if (!function.CanPInvoke && function.IsNative)
                    {
                        file.WriteLine("\t\tprivate " + options.FunctionsClassName + "." + function.NativeName + " _" + function.NativeName + ";");
                        file.WriteLine();
                    }

                    file.WriteLine(this.GetDotNetDeclaration("\t\t", function, options));
                    file.WriteLine();
                }

                file.WriteLine("\t}");

                file.WriteLine("}");
            }
        }
        public void WriteOverloadsFile(DotNetApiData api, Options options)
        {
            string fileName = Path.Combine(options.OutputPath, "GLContext.Overloads.Generated.cs");

            StringBuilder sb = new StringBuilder(1024);

            foreach (string template in Directory.EnumerateFiles(Path.Combine("Templates", "Overloads")).Select(x => Path.GetFileNameWithoutExtension(x)))
            {
                if (api.Commands.Any(x => x.DotNetName == template))
                {
                    sb.AppendLine(File.ReadAllText(Path.Combine("Templates", "Overloads", template + ".tmpl")).TrimEnd());
                    sb.AppendLine();
                }
            }

            TemplateEngine.Run("GLContext.Overloads", fileName, new Dictionary<string, string>
            {
                { "Namespace", options.Namespace },
                { "Overloads", sb.ToString().TrimEnd()  }
            });
        }
		public void WriteContextFile(DotNetApiData api, Options options)
		{
			string fileName = Path.Combine(options.OutputPath, options.ContextFileName);
			using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
			{
				file.WriteLine("using System;");
				file.WriteLine("using System.Runtime.InteropServices;");
				file.WriteLine("using System.Text;");
				file.WriteLine();
				file.WriteLine("namespace {0}", options.Namespace);
				file.WriteLine("{");

				file.WriteLine("\tpublic partial class {0}", options.ContextClassName);
				file.WriteLine("\t{");

                var specCommands = GetSpecCommands(api);

                foreach (var command in specCommands)
                {
                    file.WriteLine("\t\tprivate " + options.CommandsClassName + "." + command.NativeName + " _" + command.NativeName + ";");
                    file.WriteLine();                    
                }

                if (options.ContextInitializeDelegatesByVersionNumber)
                    file.WriteLine("\t\tprivate void InitializeCommands(int versionMajor, int versionMinor)");
                else
                    file.WriteLine("\t\tprivate void InitializeCommands()");

                file.WriteLine("\t\t{");

				int versionMajor = -1;
				int versionMinor = -1;
                
                if (options.ContextInitializeDelegatesByVersionNumber)
                {
                    foreach (var function in specCommands)
                    {
                        if (versionMajor != function.VersionMajor || versionMinor != function.VersionMinor)
                        {
                            if (versionMajor != -1)
                            {
                                file.WriteLine("\t\t\t}");
                                file.WriteLine();
                            }

                            versionMajor = function.VersionMajor;
                            versionMinor = function.VersionMinor;
                            file.WriteLine("\t\t\tif (versionMajor > {0} || (versionMajor == {0} && versionMinor >= {1}))", versionMajor, versionMinor);
                            file.WriteLine("\t\t\t{");
                        }

                        file.WriteLine("\t\t\t\tthis._" + function.NativeName + " = (" + options.CommandsClassName + "." + function.NativeName + ")this.LoadCommand<" + options.CommandsClassName + "." + function.NativeName + ">();");
                    }

                    file.WriteLine("\t\t\t}");
                }
                else
                {
                    foreach (var function in specCommands)
                    {
                        file.WriteLine("\t\t\tthis._" + function.NativeName + " = (" + options.CommandsClassName + "." + function.NativeName + ")this.LoadCommand<" + options.CommandsClassName + "." + function.NativeName + ">();");
                    }
                }

				file.WriteLine("\t\t}");
				file.WriteLine();

				foreach (var function in api.Commands)
				{
					file.WriteLine(this.GetDotNetDeclaration("\t\t", function, options));
					file.WriteLine();
				}

				file.WriteLine("\t}");

				file.WriteLine("}");
			}
        }
		public void WriteCommandsFile(DotNetApiData api, Options options)
		{
			string fileName = Path.Combine(options.OutputPath, options.CommandsFileName);
			using (StreamWriter file = new StreamWriter(File.Open(fileName, FileMode.Create)))
			{
				file.WriteLine("using System;");
				file.WriteLine("using System.Runtime.InteropServices;");
                file.WriteLine("using System.Text;");
                file.WriteLine();
                file.WriteLine("#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member");
                file.WriteLine();
				file.WriteLine("namespace {0}", options.Namespace);
				file.WriteLine("{");

				file.WriteLine("\tpublic static partial class {0}", options.CommandsClassName);
				file.WriteLine("\t{");
                
				foreach (var function in GetSpecCommands(api))
				{
                    file.WriteLine(this.GetDelegateDeclaration("\t\t", function));
					file.WriteLine();
				}

				file.WriteLine("\t}");

				file.WriteLine("}");
			}
		}