local ffi = require("ffi")
local C = ffi.C

local core = {}

function core.init(gfx, input, rng, sfx, sys)
    core.gfx = gfx
    core.input = input
    core.rng = rng
    core.sfx = sfx
    core.sys = sys
end

function core.find_color(color)
    return C.pxl8_gfx_find_color(core.gfx, color)
end

function core.get_fps()
    return C.pxl8_get_fps(core.sys)
end

function core.palette_color(index)
    local pal = C.pxl8_gfx_palette(core.gfx)
    if pal == nil then return 0 end
    return C.pxl8_palette_color(pal, index)
end

function core.palette_index(color)
    local pal = C.pxl8_gfx_palette(core.gfx)
    if pal == nil then return -1 end
    return C.pxl8_palette_index(pal, color)
end

function core.set_palette_rgb(index, r, g, b)
    local pal = C.pxl8_gfx_palette(core.gfx)
    if pal == nil then return end
    C.pxl8_palette_set_rgb(pal, index, r, g, b)
end

function core.set_palette(colors, count)
    C.pxl8_gfx_set_palette_colors(core.gfx, colors, count)
end

function core.set_colormap(data, size)
    local cm = C.pxl8_gfx_colormap(core.gfx)
    if cm == nil then return end
    C.pxl8_set_colormap(cm, data, size)
end

function core.update_palette_deps()
    C.pxl8_gfx_blend_tables_update(core.gfx)
    C.pxl8_gfx_colormap_update(core.gfx)
end

function core.ramp_index(position)
    local pal = C.pxl8_gfx_palette(core.gfx)
    if pal == nil then return 0 end
    return C.pxl8_palette_ramp_index(pal, position)
end

function core.get_width()
    return C.pxl8_gfx_get_width(core.gfx)
end

function core.get_height()
    return C.pxl8_gfx_get_height(core.gfx)
end

function core.get_title()
    local cart = C.pxl8_get_cart()
    if cart ~= nil then
        local title = C.pxl8_cart_get_title(cart)
        if title ~= nil then
            return ffi.string(title)
        end
    end
    return "pxl8"
end

local function is_user_script(info)
    local src = info and info.short_src
    return src and (src:match("%.fnl$") or src:match("%.lua$"))
end

local function get_caller_info()
    for level = 2, 10 do
        local info = debug.getinfo(level, "Sl")
        if not info then break end
        if is_user_script(info) then
            return info.short_src, info.currentline or 0
        end
    end
    return "?", 0
end

core.LOG_TRACE = 0
core.LOG_DEBUG = 1
core.LOG_INFO = 2
core.LOG_WARN = 3
core.LOG_ERROR = 4

local function make_logger(level)
    return function(msg)
        local src, line = get_caller_info()
        C.pxl8_lua_log(level, src, line, msg)
    end
end

core.trace = make_logger(core.LOG_TRACE)
core.debug = make_logger(core.LOG_DEBUG)
core.info = make_logger(core.LOG_INFO)
core.warn = make_logger(core.LOG_WARN)
core.error = make_logger(core.LOG_ERROR)

function core.quit()
    C.pxl8_set_running(core.sys, false)
end

function core.rng_seed(seed)
    C.pxl8_rng_seed(core.rng, seed)
end

function core.rng_next()
    return C.pxl8_rng_next(core.rng)
end

function core.rng_f32()
    return C.pxl8_rng_f32(core.rng)
end

function core.rng_range(min, max)
    return C.pxl8_rng_range(core.rng, min, max)
end

return core
