#if COMPILER_MSVC

/* ========================== *
 * Msvc RC file lookup
 * ========================== */

#include "incbin.h"
#include "scratch.h"
#include "string.h"
#include "atomic.h"
#include "intrinsics.h"

#define WIN32_LEAN_AND_MEAN
#define UNICODE
#include <Windows.h>

struct rc_search_params {
    /* In */
    struct string name_lower;
    /* Out */
    b32 found;
    struct buffer data;
};

/* Find first resource with `type` and return the data in `udata`. */
INTERNAL BOOL CALLBACK enum_func(HMODULE module, LPCWSTR type, LPCWSTR wstr_entry_name, LONG_PTR udata)
{
    struct temp_arena scratch = scratch_begin_no_conflict();

    struct rc_search_params *params = (struct rc_search_params *)udata;

    struct string entry_name_lower = string_lower(scratch.arena, string_from_wstr(scratch.arena, (LPWSTR)wstr_entry_name));

    params->found = false;
    params->data = BUFFER(0, 0);
    if (string_eq(entry_name_lower, params->name_lower)) {
        HRSRC hres = FindResourceW(module, wstr_entry_name, type);
        if (hres) {
            HGLOBAL hg = LoadResource(module, hres);
            if (hg) {
                params->found = true;
                params->data.size = SizeofResource(module, hres);
                params->data.data = LockResource(hg);
            }
        }
    }

    scratch_end(scratch);
	return params->found;
}

struct buffer _incbin_get(struct _incbin_rc_resource *inc)
{
    enum inc_state state = atomic_i32_eval(&inc->state);
    if (state == INCBIN_STATE_SEARCHED) {
        return inc->data;
    }

    if (state == INCBIN_STATE_UNSEARCHED) {
        enum inc_state v = atomic_i32_eval_compare_exchange(&inc->state, state, INCBIN_STATE_SEARCHING);
        if (v == state) {
            struct temp_arena scratch = scratch_begin_no_conflict();
            {
                /* Search */
                struct string name_lower = string_lower(scratch.arena, inc->rc_name);
                struct rc_search_params params = { .name_lower = name_lower };
                EnumResourceNamesW(NULL, RT_RCDATA, &enum_func, (LONG_PTR)&params);
                if (!params.found) {
                    sys_panic(string_format(scratch.arena,
                                            STR("INCBIN include not found in RC file: \"%F\""),
                                            FMT_STR(inc->rc_name)));
                }
                inc->data = params.data;
                state = INCBIN_STATE_SEARCHED;
                atomic_i32_eval_exchange(&inc->state, state);
            }
            scratch_end(scratch);
        } else {
            state = v;
        }
    }

    /* Spin while another thread is searching */
    while (state != INCBIN_STATE_SEARCHED) {
        ix_pause();
        state = atomic_i32_eval(&inc->state);
    }

    return inc->data;
}

#endif
