
    
#include "description_parser.h"
#include "processor.h"

#if 0
#include "gfx/gfx.h"
#include "runtime/buffer_manager.h"

#include <stdio.h>
#include <string.h>

static int RenderTargetExists(const rt_framegraph_info *framegraph, rt_render_target_id id) {
    const rt_render_target_info *render_targets = rtResolveConstRelptr(&framegraph->render_targets);
    for (uint32_t i = 0; i < framegraph->render_target_count; ++i) {
        if (render_targets[i].id == id)
            return 1;
    }
    return 0;
}

static rt_result ParseFramegraph(const char *text,
                                 size_t size,
                                 const char *file_path,
                                 rt_framegraph_info **p_framegraph,
                                 rt_arena *arena) {
    rt_parse_state state;
    unsigned int root_list;
    rt_result result = rtParseDescription(text, size, file_path, &root_list, &state, arena);
    if (result != RT_SUCCESS)
        return result;

    /* Push on the arena to ensure a continous memory layout */
    rt_framegraph_info *framegraph = RT_ARENA_PUSH_STRUCT_ZERO(arena, rt_framegraph_info);
    if (!framegraph)
        return RT_OUT_OF_MEMORY;

    /* ~~~~ Render Targets ~~~~ */
    const rt_parsed_stmt *rt_list_stmt = rtFindStatement(&state, root_list, "render_targets");
    if (!rt_list_stmt) {
        rtLog("AC",
              "Framegraph %s does not contain a list of render targets. (\"render_targets\")",
              file_path);
        return RT_INVALID_VALUE;
    }
    if (rt_list_stmt->form != RT_STMT_FORM_LIST) {
        rtLog("AC", "Expected a list as the value of \"render_targets\" in %s", file_path);
        return RT_INVALID_VALUE;
    }
    const rt_parsed_stmt_list *rt_list = &state.statement_lists[rt_list_stmt->list_index];
    framegraph->render_target_count    = rt_list->count;

    rt_render_target_info *render_targets =
        RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_info, framegraph->render_target_count);
    if (!render_targets)
        return RT_OUT_OF_MEMORY;
    rtSetRelptr(&framegraph->render_targets, render_targets);
    const rt_parsed_stmt *rendertarget_stmt = &state.statements[rt_list->first];
    for (uint32_t i = 0; i < rt_list->count;
         ++i, rendertarget_stmt = &state.statements[rendertarget_stmt->next]) {
        if (rendertarget_stmt->form != RT_STMT_FORM_LIST) {
            rtLog("AC",
                  "Expected a list as the value of \"render_targets.%.*s\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }

        rt_render_target_info rt = {0};
        rt.id                    = rtCalculateRenderTargetID(rendertarget_stmt->attribute.start,
                                          rendertarget_stmt->attribute.length);
        framegraph->names_size += rendertarget_stmt->attribute.length;

        const rt_parsed_stmt *width_stmt =
            rtFindStatement(&state, rendertarget_stmt->list_index, "width");
        if (!width_stmt) {
            rtLog("AC",
                  "Could not find \"render_targets.%.*s.width\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (width_stmt->form != RT_STMT_FORM_VALUE) {
            rtLog("AC",
                  "Expected a simple value for \"render_targets.%.*s.width\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (rtCompareSpanToString(width_stmt->value, "SWAPCHAIN") == 0)
            rt.width = RT_RENDER_TARGET_SIZE_SWAPCHAIN;
        else
            sscanf(width_stmt->value.start, "%u", &rt.width);

        const rt_parsed_stmt *height_stmt =
            rtFindStatement(&state, rendertarget_stmt->list_index, "height");
        if (!height_stmt) {
            rtLog("AC",
                  "Could not find \"render_targets.%.*s.height\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (height_stmt->form != RT_STMT_FORM_VALUE) {
            rtLog("AC",
                  "Expected a simple value for \"render_targets.%.*s.height\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (rtCompareSpanToString(height_stmt->value, "SWAPCHAIN") == 0)
            rt.height = RT_RENDER_TARGET_SIZE_SWAPCHAIN;
        else
            sscanf(height_stmt->value.start, "%u", &rt.height);

        const rt_parsed_stmt *samples_stmt =
            rtFindStatement(&state, rendertarget_stmt->list_index, "sample_count");
        if (!samples_stmt) {
            rtLog("AC",
                  "Could not find \"render_targets.%.*s.sample_count\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (samples_stmt->form != RT_STMT_FORM_VALUE) {
            rtLog("AC",
                  "Expected a simple value for \"render_targets.%.*s.sample_count\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        sscanf(samples_stmt->value.start, "%u", &rt.sample_count);

        const rt_parsed_stmt *format_stmt =
            rtFindStatement(&state, rendertarget_stmt->list_index, "format");
        if (!format_stmt) {
            rtLog("AC",
                  "Could not find \"render_targets.%.*s.format\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (format_stmt->form != RT_STMT_FORM_VALUE) {
            rtLog("AC",
                  "Expected a simple value for \"render_targets.%.*s.format\" in %s",
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }
        if (rtCompareSpanToString(format_stmt->value, "R8G8B8A8_UNORM") == 0)
            rt.format = RT_PIXEL_FORMAT_R8G8B8A8_UNORM;
        else if (rtCompareSpanToString(format_stmt->value, "B8G8R8A8_UNORM") == 0)
            rt.format = RT_PIXEL_FORMAT_B8G8R8A8_UNORM;
        else if (rtCompareSpanToString(format_stmt->value, "R8G8B8A8_SRGB") == 0)
            rt.format = RT_PIXEL_FORMAT_R8G8B8A8_SRGB;
        else if (rtCompareSpanToString(format_stmt->value, "B8G8R8A8_SRGB") == 0)
            rt.format = RT_PIXEL_FORMAT_B8G8R8A8_SRGB;
        else if (rtCompareSpanToString(format_stmt->value, "R8G8B8_UNORM") == 0)
            rt.format = RT_PIXEL_FORMAT_R8G8B8_UNORM;
        else if (rtCompareSpanToString(format_stmt->value, "B8G8R8_UNORM") == 0)
            rt.format = RT_PIXEL_FORMAT_B8G8R8_UNORM;
        else if (rtCompareSpanToString(format_stmt->value, "R8G8B8_SRGB") == 0)
            rt.format = RT_PIXEL_FORMAT_R8G8B8_SRGB;
        else if (rtCompareSpanToString(format_stmt->value, "B8G8R8_SRGB") == 0)
            rt.format = RT_PIXEL_FORMAT_B8G8R8_SRGB;
        else if (rtCompareSpanToString(format_stmt->value, "DEPTH24_STENCIL8") == 0)
            rt.format = RT_PIXEL_FORMAT_DEPTH24_STENCIL8;
        else if (rtCompareSpanToString(format_stmt->value, "DEPTH32") == 0)
            rt.format = RT_PIXEL_FORMAT_DEPTH32;
        else if (rtCompareSpanToString(format_stmt->value, "SWAPCHAIN") == 0)
            rt.format = RT_PIXEL_FORMAT_SWAPCHAIN;
        else {
            rt.format = RT_PIXEL_FORMAT_INVALID;
            rtLog("AC",
                  "Invalid format value \"%.*s\" in \"render_targets.%.*s.format\" in %s",
                  format_stmt->value.length,
                  format_stmt->value.start,
                  rendertarget_stmt->attribute.length,
                  rendertarget_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }

        render_targets[i] = rt;
    }

    /* ~~~~ Render Passes ~~~~ */
    /* First count the total number of reads and writes */
    uint32_t total_reads = 0, total_writes = 0;
    const rt_parsed_stmt *pass_list_stmt = rtFindStatement(&state, root_list, "passes");
    if (!pass_list_stmt) {
        rtLog("Framegraph %s does not contain a list of passes (\"passes\")", file_path);
        return RT_INVALID_VALUE;
    }
    if (pass_list_stmt->form != RT_STMT_FORM_LIST) {
        rtLog("AC", "Expected a list as the value of \"passes\" in %s", file_path);
        return RT_INVALID_VALUE;
    }
    const rt_parsed_stmt_list *pass_list = &state.statement_lists[pass_list_stmt->list_index];
    rt_render_pass_info *passes =
        RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_pass_info, pass_list->count);
    if (!passes)
        return RT_OUT_OF_MEMORY;
    rtSetRelptr(&framegraph->render_passes, passes);
    framegraph->render_pass_count   = pass_list->count;
    const rt_parsed_stmt *pass_stmt = &state.statements[pass_list->first];
    for (uint32_t i = 0; i < pass_list->count;
         ++i, pass_stmt = &state.statements[pass_stmt->next]) {
        if (pass_stmt->form != RT_STMT_FORM_LIST) {
            rtLog("AC",
                  "Expected a list as the value of \"passes.%.*s\" in %s",
                  pass_stmt->attribute.length,
                  pass_stmt->attribute.start,
                  file_path);
            return RT_INVALID_VALUE;
        }

        framegraph->names_size += pass_stmt->attribute.length + 1;

        passes[i].id =
            rtCalculateRenderPassID(pass_stmt->attribute.start, pass_stmt->attribute.length);
        passes[i].type                  = RT_RENDER_PASS_TYPE_GRAPHICS;
        const rt_parsed_stmt *type_stmt = rtFindStatement(&state, pass_stmt->list_index, "type");
        if (type_stmt) {
            if (type_stmt->form != RT_STMT_FORM_VALUE) {
                rtLog("AC",
                      "Expected GRAPHICS or COMPUTE as the value of \"passes.%.*s.type\" in %s",
                      pass_stmt->attribute.length,
                      pass_stmt->attribute.start,
                      file_path);
                return RT_INVALID_VALUE;
            }
            if (rtCompareSpanToString(type_stmt->value, "GRAPHICS") == 0) {
                passes[i].type = RT_RENDER_PASS_TYPE_GRAPHICS;
            } else if (rtCompareSpanToString(type_stmt->value, "COMPUTE") == 0) {
                passes[i].type = RT_RENDER_PASS_TYPE_COMPUTE;
            } else {
                rtLog("AC",
                      "Expected GRAPHICS or COMPUTE as the value of \"passes.%.*s.type\" in %s",
                      pass_stmt->attribute.length,
                      pass_stmt->attribute.start,
                      file_path);
                return RT_INVALID_VALUE;
            }
        }

        const rt_parsed_stmt *write_list_stmt =
            rtFindStatement(&state, pass_stmt->list_index, "writes");
        if (write_list_stmt) {
            if (write_list_stmt->form != RT_STMT_FORM_LIST) {
                rtLog("AC",
                      "Expected a list as the value of \"passes.%.*s.writes\" in %s",
                      pass_stmt->attribute.length,
                      pass_stmt->attribute.start,
                      file_path);
                return RT_INVALID_VALUE;
            }
            const rt_parsed_stmt_list *write_list =
                &state.statement_lists[write_list_stmt->list_index];
            passes[i].write_render_target_count = write_list->count;
            total_writes += write_list->count;
        } else {
            passes[i].write_render_target_count = 0;
            rtSetRelptr(&passes[i].write_render_targets, NULL);
        }

        const rt_parsed_stmt *read_list_stmt =
            rtFindStatement(&state, pass_stmt->list_index, "reads");
        if (read_list_stmt) {
            if (read_list_stmt->form != RT_STMT_FORM_LIST) {
                rtLog("AC",
                      "Expected a list as the value of \"passes.%.*s.reads\" in %s",
                      pass_stmt->attribute.length,
                      pass_stmt->attribute.start,
                      file_path);
                return RT_INVALID_VALUE;
            }
            const rt_parsed_stmt_list *read_list =
                &state.statement_lists[read_list_stmt->list_index];
            passes[i].read_render_target_count = read_list->count;
            total_reads += read_list->count;
        } else {
            passes[i].read_render_target_count = 0;
            rtSetRelptr(&passes[i].read_render_targets, NULL);
        }
    }
    /* Now fill the read & write lists */
    rt_render_target_read *reads =
        RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_read, total_reads);
    rt_render_target_write *writes =
        RT_ARENA_PUSH_ARRAY_ZERO(arena, rt_render_target_write, total_writes);
    if (!reads && total_reads != 0)
        return RT_OUT_OF_MEMORY;
    if (!writes && total_writes != 0)
        return RT_OUT_OF_MEMORY;
    uint32_t reads_at  = 0;
    uint32_t writes_at = 0;

    pass_stmt = &state.statements[pass_list->first];
    for (uint32_t i = 0; i < pass_list->count;
         ++i, pass_stmt = &state.statements[pass_stmt->next]) {
        RT_ASSERT(pass_stmt->form == RT_STMT_FORM_LIST, "");
        const rt_parsed_stmt *write_list_stmt =
            rtFindStatement(&state, pass_stmt->list_index, "writes");
        if (write_list_stmt) {
            RT_ASSERT(write_list_stmt->form == RT_STMT_FORM_LIST, "");
            const rt_parsed_stmt_list *write_list =
                &state.statement_lists[write_list_stmt->list_index];

            rtSetRelptr(&passes[i].write_render_targets, &writes[writes_at]);
            const rt_parsed_stmt *write_stmt = &state.statements[write_list->first];
            for (uint32_t j = 0; j < write_list->count;
                 ++j, write_stmt = &state.statements[write_stmt->next]) {

                if (write_stmt->form != RT_STMT_FORM_LIST) {
                    rtLog("AC",
                          "Expected a list as the value of \"passes.%.*s.writes.%.*s\" in %s",
                          pass_stmt->attribute.length,
                          pass_stmt->attribute.start,
                          write_stmt->attribute.length,
                          write_stmt->attribute.start,
                          file_path);
                    return RT_INVALID_VALUE;
                }

                rt_render_target_write *write = &writes[writes_at++];
                memset(write, 0, sizeof(*write));
                write->render_target = rtCalculateRenderTargetID(write_stmt->attribute.start,
                                                                 write_stmt->attribute.length);
                if (!RenderTargetExists(framegraph, write->render_target)) {
                    rtLog("AC",
                          "Referenced unknown render target \"%.s\" in \"passes.%.*s.writes.%.*s\" "
                          "in %s",
                          write_stmt->attribute.length,
                          write_stmt->attribute.start,
                          pass_stmt->attribute.length,
                          pass_stmt->attribute.start,
                          file_path);
                    return RT_INVALID_VALUE;
                }

                const rt_parsed_stmt *clear_value_stmt =
                    rtFindStatement(&state, write_stmt->list_index, "clear_value");
                if (clear_value_stmt) {
                    if (clear_value_stmt->form != RT_STMT_FORM_LIST) {
                        rtLog("AC",
                              "Expected a list as the value of "
                              "\"passes.%.*s.writes.%.*s.clear_value\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              write_stmt->attribute.length,
                              write_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                    const rt_parsed_stmt *r_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "r");
                    if (r_stmt) {
                        if (r_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected a float as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.r\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(r_stmt->value.start, "%f", &write->clear.color.r);
                    }
                    const rt_parsed_stmt *g_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "g");
                    if (g_stmt) {
                        if (g_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected a float as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.g\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(g_stmt->value.start, "%f", &write->clear.color.g);
                    }
                    const rt_parsed_stmt *b_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "b");
                    if (b_stmt) {
                        if (b_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected a float as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.b\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(b_stmt->value.start, "%f", &write->clear.color.b);
                    }
                    const rt_parsed_stmt *a_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "a");
                    if (a_stmt) {
                        if (a_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected a float as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.a\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(a_stmt->value.start, "%f", &write->clear.color.a);
                    }
                    const rt_parsed_stmt *d_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "d");
                    if (d_stmt) {
                        if (d_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected a float as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.d\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(d_stmt->value.start, "%f", &write->clear.depth_stencil.depth);
                    }
                    const rt_parsed_stmt *s_stmt =
                        rtFindStatement(&state, clear_value_stmt->list_index, "s");
                    if (s_stmt) {
                        if (s_stmt->form != RT_STMT_FORM_VALUE) {
                            rtLog("AC",
                                  "Expected an integer as the value of "
                                  "\"passes.%.*s.writes.%.*s.clear_value.s\" in %s",
                                  pass_stmt->attribute.length,
                                  pass_stmt->attribute.start,
                                  write_stmt->attribute.length,
                                  write_stmt->attribute.start,
                                  file_path);
                            return RT_INVALID_VALUE;
                        }
                        sscanf(s_stmt->value.start, "%d", &write->clear.depth_stencil.stencil);
                    }
                }

                const rt_parsed_stmt *clear_stmt =
                    rtFindStatement(&state, write_stmt->list_index, "clear");
                if (clear_stmt) {
                    if (clear_stmt->form != RT_STMT_FORM_VALUE) {
                        rtLog("AC",
                              "Expected a YES or NO as the value of "
                              "\"passes.%.*s.writes.%.*s.clear\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              write_stmt->attribute.length,
                              write_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                    if (rtCompareSpanToString(clear_stmt->value, "YES") == 0) {
                        write->flags |= RT_RENDER_TARGET_WRITE_CLEAR;
                    } else if (rtCompareSpanToString(clear_stmt->value, "NO") != 0) {
                        rtLog("AC",
                              "Expected a YES or NO as the value of "
                              "\"passes.%.*s.writes.%.*s.clear\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              write_stmt->attribute.length,
                              write_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                }

                const rt_parsed_stmt *discard_stmt =
                    rtFindStatement(&state, write_stmt->list_index, "discard");
                if (discard_stmt) {
                    if (discard_stmt->form != RT_STMT_FORM_VALUE) {
                        rtLog("AC",
                              "Expected YES or NO as the value of "
                              "\"passes.%.*s.writes.%.*s.clear\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              write_stmt->attribute.length,
                              write_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                    if (rtCompareSpanToString(discard_stmt->value, "YES") == 0) {
                        write->flags |= RT_RENDER_TARGET_WRITE_DISCARD;
                    } else if (rtCompareSpanToString(discard_stmt->value, "NO") != 0) {
                        rtLog("AC",
                              "Expected YES or NO as the value of "
                              "\"passes.%.*s.writes.%.*s.discard\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              write_stmt->attribute.length,
                              write_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                }
            }
        }

        const rt_parsed_stmt *read_list_stmt =
            rtFindStatement(&state, pass_stmt->list_index, "reads");
        if (read_list_stmt) {
            RT_ASSERT(read_list_stmt->form == RT_STMT_FORM_LIST, "");
            const rt_parsed_stmt_list *read_list =
                &state.statement_lists[read_list_stmt->list_index];

            rtSetRelptr(&passes[i].read_render_targets, &reads[reads_at]);
            const rt_parsed_stmt *read_stmt = &state.statements[read_list->first];
            for (uint32_t j = 0; j < read_list->count;
                 ++j, read_stmt = &state.statements[read_stmt->next]) {

                if (read_stmt->form != RT_STMT_FORM_LIST) {
                    rtLog("AC",
                          "Expected a list as the value of \"passes.%.*s.reads.%.*s\" in %s",
                          pass_stmt->attribute.length,
                          pass_stmt->attribute.start,
                          read_stmt->attribute.length,
                          read_stmt->attribute.start,
                          file_path);
                    return RT_INVALID_VALUE;
                }

                rt_render_target_read *read = &reads[reads_at++];
                memset(read, 0, sizeof(*read));
                read->render_target = rtCalculateRenderTargetID(read_stmt->attribute.start,
                                                                read_stmt->attribute.length);
                if (!RenderTargetExists(framegraph, read->render_target)) {
                    rtLog("AC",
                          "Referenced unknown render target \"%.s\" in \"passes.%.*s.reads.%.*s\" "
                          "in %s",
                          read_stmt->attribute.length,
                          read_stmt->attribute.start,
                          pass_stmt->attribute.length,
                          pass_stmt->attribute.start,
                          file_path);
                    return RT_INVALID_VALUE;
                }

                const rt_parsed_stmt *mode_stmt =
                    rtFindStatement(&state, read_stmt->list_index, "mode");
                if (mode_stmt) {
                    if (mode_stmt->form != RT_STMT_FORM_VALUE) {
                        rtLog("AC",
                              "Expected SAMPLED or INPUT_ATTACHMENT as the value of "
                              "\"passes.%.*s.writes.%.*s.mode\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              read_stmt->attribute.length,
                              read_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                    if (rtCompareSpanToString(mode_stmt->value, "SAMPLED") == 0) {
                        read->mode = RT_RENDER_TARGET_READ_SAMPLED;
                    } else if (rtCompareSpanToString(mode_stmt->value, "DIRECT") == 0) {
                        read->mode = RT_RENDER_TARGET_READ_DIRECT;
                    } else {
                        rtLog("AC",
                              "Expected SAMPLED or DIRECT as the value of "
                              "\"passes.%.*s.writes.%.*s.mode\" in %s",
                              pass_stmt->attribute.length,
                              pass_stmt->attribute.start,
                              read_stmt->attribute.length,
                              read_stmt->attribute.start,
                              file_path);
                        return RT_INVALID_VALUE;
                    }
                }
            }
        }
    }

    /* ~~~~ Names ~~~~ */
    char *names = rtArenaPush(arena, framegraph->names_size);
    rtSetRelptr(&framegraph->names, names);
    char *names_at = names;
    pass_stmt      = &state.statements[pass_list->first];
    for (uint32_t i = 0; i < pass_list->count;
         ++i, pass_stmt = &state.statements[pass_stmt->next]) {
        if (names) {
            memcpy(names_at, pass_stmt->attribute.start, pass_stmt->attribute.length);
            names_at[pass_stmt->attribute.length] = '\0';
            rtSetRelptr(&passes[i].name, names_at);
            passes[i].name_len = pass_stmt->attribute.length + 1;
            names_at += pass_stmt->attribute.length + 1;
        } else {
            rtSetRelptr(&passes[i].name, NULL);
            passes[i].name_len = 0;
        }
    }

    rendertarget_stmt = &state.statements[rt_list->first];
    for (uint32_t i = 0; i < rt_list->count;
         ++i, rendertarget_stmt = &state.statements[rendertarget_stmt->next]) {
        if (names) {
            memcpy(names_at,
                   rendertarget_stmt->attribute.start,
                   rendertarget_stmt->attribute.length);
            names_at[rendertarget_stmt->attribute.length] = '\0';
            rtSetRelptr(&render_targets[i].name, names_at);
            render_targets[i].name_len = rendertarget_stmt->attribute.length + 1;
            names_at += rendertarget_stmt->attribute.length + 1;
        } else {
            rtSetRelptr(&render_targets[i].name, NULL);
            render_targets[i].name_len = 0;
        }
    }

    *p_framegraph = framegraph;
    return result;
}

RT_ASSET_PROCESSOR_FN(FramegraphProcessor) {
    rt_loaded_asset asset = LoadAsset(file);
    if (!asset.buffer)
        return RT_UNKNOWN_ERROR;

    rt_framegraph_info *framegraph = NULL;
    rt_result result =
        ParseFramegraph(asset.buffer, asset.size, rtGetFilePath(file), &framegraph, arena);
    if (result != RT_SUCCESS)
        goto out;

    rt_resource resource = {0};
    resource.type        = RT_RESOURCE_FRAMEGRAPH;
    resource.data        = framegraph;

    rt_resource_id id;
    const char *name = rtGetFilePath(file);
    result           = rtCreateResources(1, &name, &resource, &id);
    if (result != RT_SUCCESS)
        goto out;

    new_resources[0]    = id;
    *new_resource_count = 1;

out:
    rtReleaseBuffer(asset.buffer, asset.size);
    return result;
}
#endif