public static async Task EmitScope(this CodegenContext context, Action cb)
        {
            await context.EmitLine("{");

            ++context.IndentLevel;
            cb();
            --context.IndentLevel;
            await context.EmitLine("}");
        }
        private static async Task EmitClassStructEnumBlock(this CodegenContext context, string type, string name, Func <Task> cb)
        {
            await context.EmitLine($"{type} {name}");

            await context.EmitLine("{");

            ++context.IndentLevel;
            await cb();

            --context.IndentLevel;
            await context.EmitLine("};");
        }
        public static async Task EmitStructAccess(this CodegenContext context, string access)
        {
            if (!access.EndsWith(':'))
            {
                access = access + ":";
            }

            --context.IndentLevel;
            await context.EmitLine(access);

            ++context.IndentLevel;
        }
        public async Task GenerateCommandBufferHeader(string bufferClass, string fileName, List <KeyValuePair <string, FunctionEntry> > functionList)
        {
            using (var header = CreateHeader(fileName))
            {
                var context = new CodegenContext(header);
                await context.EmitLine("#pragma once");

                await context.EmitLine("#include <glad/glad.h>");

                await context.EmitLine("#include \"gl_resource_manager.hpp\"");

                await context.EmitLine("#include \"raw_rw_buffer.hpp\"");

                if (Registry.EnumTypes.Count > 0)
                {
                    await context.EmitLine("#include \"gl_function_enums.hpp\"");
                }

                context.EmitLine();
                await context.EmitLine("namespace multigl");

                await context.EmitScope(async() =>
                {
                    await context.EmitClass(bufferClass, async() =>
                    {
                        await context.EmitStructAccess("public");
                        await context.EmitLine($"{bufferClass}(ResourceManager& manager);");
                        await context.EmitLine($"~{bufferClass}();");
                        context.EmitLine();

                        await context.EmitStructAccess("public");

                        var accessTracker = context.CreateAccessTracker("public");
                        foreach (var fk in functionList)
                        {
                            var function = fk.Value;
                            await accessTracker.WriteAccess(function.Access);

                            var noGLName = function.NoGLName;

                            if (!function.ShouldReturnAsFinalArgPointer)
                            {
                                context.EmitIndent();
                                await context.Emit($"{function.Type.ReturnType} {noGLName}(");
                                for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                {
                                    var arg      = function.Type.Arguments[i];
                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                    await context.Emit($"{fullType} {arg.Name}");
                                    if (i < function.Type.Arguments.Count - 1)
                                    {
                                        await context.Emit(", ");
                                    }
                                }
                                await context.EmitLineUnindented(");");
                            }
                            else
                            {
                                context.EmitIndent();
                                await context.Emit($"void {noGLName}(");
                                for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                {
                                    var arg = function.Type.Arguments[i];
                                    await context.Emit($"{arg.Type} {arg.Name}");
                                    await context.Emit(", ");
                                }
                                await context.Emit($"{function.Type.ReturnType}* returnVal = 0");
                                await context.EmitLineUnindented(");");
                            }
                        }
                        context.EmitLine();

                        await context.EmitStructAccess("public");
                        await context.EmitLine("void ProcessCommands();");
                        context.EmitLine();

                        await context.EmitStructAccess("private");
                        await context.EmitLine($"ResourceManager& {ResourceManager};");
                        await context.EmitLine($"raw_rw_buffer {DataBuffer};");
                        if (bufferClass == ImmediateBufferClass)
                        {
                            await context.EmitLine("bool m_HasSubmittedWork;");
                        }
                    });
                });
            }
        }
        public async Task GenerateEnumTypeHeader()
        {
            string enumType  = null;
            int    numValues = Registry.Functions.Count;

            if (numValues < byte.MaxValue)
            {
                enumType = "uint8_t";
            }
            else if (numValues < ushort.MaxValue)
            {
                enumType = "uint16_t";
            }
            else if ((uint)numValues < uint.MaxValue)
            {
                enumType = "uint32_t";
            }

            using (var header = CreateHeader("gl_function_enums.hpp"))
            {
                var context = new CodegenContext(header);
                await context.EmitLine("#pragma once");

                await context.EmitLine("#include <stdint.h>");

                if (Registry.EnumTypes.Count > 0)
                {
                    await context.EmitLine("#include <glad/glad.h>");
                }
                context.EmitLine();

                await context.EmitLine("namespace multigl");

                await context.EmitScope(async() =>
                {
                    await context.EmitLine($"typedef {enumType} gl_command_id_t;");
                    await context.EmitLine("namespace CommandIdEnum");
                    await context.EmitScope(async() =>
                    {
                        await context.EmitEnum("Enum : gl_command_id_t", async() =>
                        {
                            foreach (var v in Registry.Functions)
                            {
                                await context.EmitLine($"{v.Value.NoGLName},");
                            }
                            await context.EmitLine("Count");
                        });
                    });
                    await context.EmitLine("typedef CommandIdEnum::Enum CommandId;");

                    foreach (var enumType in Registry.EnumTypes)
                    {
                        context.EmitLine();
                        await context.EmitLine($"namespace {enumType.Value.Name}Enum");
                        await context.EmitScope(async() => {
                            await context.EmitEnum("Enum : GLenum", async() => {
                                int cur = 0;
                                int cnt = enumType.Value.Values.Count;
                                foreach (var v in enumType.Value.Values)
                                {
                                    context.EmitIndent();
                                    await context.Emit($"{v.Key} = {v.Value}");
                                    ++cur;
                                    if (cur < cnt)
                                    {
                                        await context.Emit(",");
                                    }
                                    context.EmitLineUnindented();
                                }
                            });
                        });
                        await context.EmitLine($"typedef {enumType.Value.Name}Enum::Enum {enumType.Value.Name};");
                    }
                });
            }
        }
        public async Task GenerateCommandBufferWriteSource(string bufferClass, string fileName, List <KeyValuePair <string, FunctionEntry> > functionList)
        {
            using (var source = CreateSourceFile(fileName))
            {
                var context = new CodegenContext(source);
                await context.EmitLine($"#include <{(bufferClass == CommandBufferClass ? "gl_command_buffer.hpp" : "gl_immediate_buffer.hpp")}>");

                context.EmitLine();

                await context.EmitLine("using namespace multigl;");

                await context.EmitLine($"{bufferClass}::{bufferClass}(ResourceManager& mgr) : {ResourceManager}(mgr)");

                if (bufferClass == ImmediateBufferClass)
                {
                    ++context.IndentLevel;
                    await context.EmitLine(", m_HasSubmittedWork(false)");

                    --context.IndentLevel;
                }
                await context.EmitScope(() => {});

                context.EmitLine();
                if (bufferClass == ImmediateBufferClass)
                {
                    await context.EmitLine($"{bufferClass}::~{bufferClass}()");

                    await context.EmitScope(async() => {
                        await context.EmitLine("if (!m_HasSubmittedWork)");
                        await context.EmitScope(async() => {
                            await context.EmitLine("// TODO: Submit queue to main thread");
                        });
                        await context.EmitLine("// TODO: Wait on job system task here");
                    });
                }
                else
                {
                    await context.EmitLine($"{bufferClass}::~{bufferClass}(){{}}");
                }

                context.EmitLine();

                foreach (var fk in functionList)
                {
                    var overrideList = Tracker.GetOverrideList(fk.Key);
                    var function     = fk.Value;

                    var noGLName = function.NoGLName;

                    if (!function.ShouldReturnAsFinalArgPointer)
                    {
                        await context.Emit($"{function.Type.ReturnType} {bufferClass}::{noGLName}(");

                        for (int i = 0; i < function.Type.Arguments.Count; ++i)
                        {
                            var arg      = function.Type.Arguments[i];
                            var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                            await context.Emit($"{fullType} {arg.Name}");

                            if (i < function.Type.Arguments.Count - 1)
                            {
                                await context.Emit(", ");
                            }
                        }
                        await context.EmitLineUnindented(")");
                    }
                    else
                    {
                        await context.Emit($"void {bufferClass}::{noGLName}(");

                        for (int i = 0; i < function.Type.Arguments.Count; ++i)
                        {
                            var arg = function.Type.Arguments[i];
                            await context.Emit($"{arg.Type} {arg.Name}");

                            await context.Emit(", ");
                        }
                        await context.Emit($"{function.Type.ReturnType}* returnVal");

                        await context.EmitLineUnindented(")");
                    }

                    await context.EmitScope(async() =>
                    {
                        await context.EmitLine($"{DataBuffer}.write_command(CommandId::{function.NoGLName});");
                        Func <Task> defaultWriteFunc = async() =>
                        {
                            if (!function.ShouldReturnAsFinalArgPointer)
                            {
                                if (function.Type.ReturnType == "void")
                                {
                                    for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                    {
                                        var arg = function.Type.Arguments[i];
                                        var argWriteOverride = Tracker.GetArgumentTypeWriteOverride(arg.Type);
                                        if (argWriteOverride != null)
                                        {
                                            await argWriteOverride(context, function, arg);
                                        }
                                        else
                                        {
                                            await context.EmitLine($"{DataBuffer}.write({arg.Name});");
                                        }
                                    }
                                }
                                else
                                {
                                    await context.EmitLine($"#if defined(MGL_STRICT_COMPILATION)");
                                    await context.EmitLine($"#error Unimplemented function with return value");
                                    await context.EmitLine($"#endif");
                                }
                            }
                            else
                            {
                                for (int i = 0; i < function.Type.Arguments.Count; ++i)
                                {
                                    var arg = function.Type.Arguments[i];
                                    var argWriteOverride = Tracker.GetArgumentTypeWriteOverride(arg.Type);
                                    if (argWriteOverride != null)
                                    {
                                        await argWriteOverride(context, function, arg);
                                    }
                                    else
                                    {
                                        await context.EmitLine($"{DataBuffer}.write({arg.Name});");
                                    }
                                }
                                await context.EmitLine($"{DataBuffer}.write(returnVal);");
                            }
                        };

                        if (overrideList == null || overrideList.Count == 0)
                        {
                            await defaultWriteFunc();
                        }
                        else
                        {
                            bool hasRun = false;
                            foreach (var v in overrideList)
                            {
                                if (v.ModifyWriteFunction != null)
                                {
                                    await v.ModifyWriteFunction(context, defaultWriteFunc, function);
                                    hasRun = true;
                                }
                            }

                            if (!hasRun)
                            {
                                await defaultWriteFunc();
                            }
                        }
                    });

                    context.EmitLine();
                }
            }
        }
        public async Task GenerateCommandBufferReadSource(string bufferClass, string fileName, List <KeyValuePair <string, FunctionEntry> > functionList)
        {
            using (var source = CreateSourceFile(fileName))
            {
                var context = new CodegenContext(source);
                await context.EmitLine($"#include <{(bufferClass == CommandBufferClass ? "gl_command_buffer.hpp" : "gl_immediate_buffer.hpp")}>");

                await context.EmitLine("#include <gl_utilities.hpp>");

                context.EmitLine();

                await context.EmitLine("using namespace multigl;");

                await context.EmitLine($"void {bufferClass}::ProcessCommands()");

                await context.EmitScope(async() =>
                {
                    await context.EmitLine($"while({DataBuffer}.has_commands())");
                    await context.EmitScope(async() =>
                    {
                        await context.EmitLine($"auto cmd = {DataBuffer}.read_command();");
                        await context.EmitLine("switch(cmd)");
                        await context.EmitScope(async() =>
                        {
                            foreach (var fk in functionList)
                            {
                                var overrideList = Tracker.GetOverrideList(fk.Key);
                                var function     = fk.Value;
                                await context.EmitLine($"case CommandId::{function.NoGLName}:");
                                await context.EmitScope(async() => {
                                    Func <Task> defaultReadFunc = async() =>
                                    {
                                        if (!function.ShouldReturnAsFinalArgPointer)
                                        {
                                            var args = function.Type.Arguments;
                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg             = args[i];
                                                var argReadOverride = Tracker.GetArgumentTypeReadOverride(arg.Type);
                                                if (argReadOverride != null)
                                                {
                                                    await argReadOverride(context, function, arg);
                                                }
                                                else
                                                {
                                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                                    await context.EmitLine($"{fullType} {arg.Name} = {DataBuffer}.read<{fullType}>();");
                                                }
                                            }
                                        }
                                        else
                                        {
                                            var args = function.Type.Arguments;
                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg             = args[i];
                                                var argReadOverride = Tracker.GetArgumentTypeReadOverride(arg.Type);
                                                if (argReadOverride != null)
                                                {
                                                    await argReadOverride(context, function, arg);
                                                }
                                                else
                                                {
                                                    var fullType = $"{(arg.IsEnumType ? "multigl::" : "")}{arg.Type}";
                                                    await context.EmitLine($"{arg.Type} {arg.Name} = {DataBuffer}.read<{fullType}>();");
                                                }
                                            }

                                            {
                                                var fullType = $"{(function.Type.IsReturnEnum ? "multigl::" : "")}{function.Type.ReturnType}";
                                                await context.EmitLine($"auto returnVal = {DataBuffer}.read<{fullType}*>();");
                                            }
                                            await context.EmitLine("if (returnVal)");
                                            await context.EmitScope(async() => {
                                                context.EmitIndent();

                                                if (function.Type.IsReturnEnum)
                                                {
                                                    await context.Emit($"GL_CHECK(*returnVal = {function.Type.ReturnType}({function.Name}(");
                                                }
                                                else
                                                {
                                                    await context.Emit($"GL_CHECK(*returnVal = ({function.Name}(");
                                                }
                                                for (int i = 0; i < args.Count; ++i)
                                                {
                                                    var arg = args[i];
                                                    await context.Emit($"{arg.Name}");
                                                    if (i < args.Count - 1)
                                                    {
                                                        await context.Emit(", ");
                                                    }
                                                }

                                                await context.EmitLineUnindented($")));");
                                                await context.EmitLine("return;");
                                            });
                                        }
                                    };

                                    if (overrideList == null || overrideList.Count == 0)
                                    {
                                        var args = function.Type.Arguments;
                                        await defaultReadFunc();
                                        context.EmitIndent();
                                        await context.Emit($"GL_CHECK({function.Name}(");

                                        for (int i = 0; i < args.Count; ++i)
                                        {
                                            var arg = args[i];
                                            await context.Emit($"{arg.Name}");
                                            if (i < args.Count - 1)
                                            {
                                                await context.Emit(", ");
                                            }
                                        }

                                        await context.EmitLineUnindented($"));");
                                    }
                                    else
                                    {
                                        bool hasRun = false;
                                        foreach (var v in overrideList)
                                        {
                                            if (v.ModifyReadFunction != null)
                                            {
                                                await v.ModifyReadFunction(context, defaultReadFunc, function);
                                                hasRun = true;
                                            }
                                        }

                                        if (!hasRun)
                                        {
                                            var args = function.Type.Arguments;
                                            await defaultReadFunc();
                                            context.EmitIndent();
                                            await context.Emit($"GL_CHECK({function.Name}(");

                                            for (int i = 0; i < args.Count; ++i)
                                            {
                                                var arg = args[i];
                                                await context.Emit($"{arg.Name}");
                                                if (i < args.Count - 1)
                                                {
                                                    await context.Emit(", ");
                                                }
                                            }

                                            await context.EmitLineUnindented($"));");
                                        }
                                    }
                                    await context.EmitLine($"break;");
                                });
                            }
                        });
                    });
                    await context.EmitLine($"{DataBuffer}.reset();");
                });
            }
        }
 public CodegenContextAccessTracker(CodegenContext ctxt, string active)
 {
     context = ctxt;
     current = active;
 }
 public static async Task EmitEnum(this CodegenContext context, string name, Func <Task> cb)
 {
     await context.EmitClassStructEnumBlock("enum", name, cb);
 }
 public static CodegenContextAccessTracker CreateAccessTracker(this CodegenContext context, string active)
 {
     return(new CodegenContextAccessTracker(context, active));
 }