const std = @import("std");

pub const c = @cImport({
    @cInclude("lua.h");
    @cInclude("lualib.h");
    @cInclude("lauxlib.h");
});

/// Generates a wrapper function with error handling for a lua CFunction
/// func should be `fn(*c.lua_State) !c_int` ()
pub fn luaFunc(comptime func: anytype) c.lua_CFunction {
    return &struct {
        fn f(l: ?*c.lua_State) callconv(.C) c_int {
            return func(l.?) catch |e| {
                var buf: [1024 * 4]u8 = undefined;
                var fbs = std.io.fixedBufferStream(&buf);
                fbs.writer().print("Zig Error: {s}\n", .{@errorName(e)}) catch @panic("OOM");
                if (@errorReturnTrace()) |ert| {
                    std.debug.writeStackTrace(
                        ert.*,
                        fbs.writer(),
                        std.heap.c_allocator,
                        std.debug.getSelfDebugInfo() catch @panic("WTF"),
                        .no_color,
                    ) catch @panic("OOM");
                }

                luaPushString(l, fbs.getWritten());
                _ = c.lua_error(l);
                unreachable;
            };
        }
    }.f;
}

/// A thin wrapper around luaL_checklstring that uses the length parameter to return a slice.
pub fn luaCheckstring(l: ?*c.lua_State, idx: c_int) []const u8 {
    var len: usize = 0;
    return c.luaL_checklstring(l, idx, &len)[0..len];
}

pub fn luaPushString(l: ?*c.lua_State, s: []const u8) void {
    c.lua_pushlstring(l, s.ptr, s.len);
}

pub fn luaToString(l: ?*c.lua_State, idx: c_int) []const u8 {
    var len: usize = 0;
    return c.lua_tolstring(l, idx, &len)[0..len];
}
