--
-- Borrowed from the LÖVE project.
-- Source: https://github.com/love2d/love/blob/main/src/modules/love/jitsetup.lua
--
-- Flags documented on bottom of http://luajit.org/running.html
--
-- Flags pasted here for convenience:
-- maxtrace    1000  Max. number of traces in the cache
-- maxrecord   4000  Max. number of recorded IR instructions
-- maxirconst  500   Max. number of IR constants of a trace
-- maxside     100   Max. number of side traces of a root trace
-- maxsnap     500   Max. number of snapshots for a trace
-- hotloop     56    Number of iterations to detect a hot loop or hot call
-- hotexit     10    Number of taken exits to start a side trace
-- tryside     4     Number of attempts to compile a side trace
-- instunroll  4     Max. unroll factor for instable loops
-- loopunroll  15    Max. unroll factor for loop ops in side traces
-- callunroll  3     Max. unroll factor for pseudo-recursive calls
-- recunroll   2     Min. unroll factor for true recursion
-- sizemcode   64    Size of each machine code area in KBytes
-- maxmcode    2048  Max. total size of all machine code areas in KBytes
--

local jit = LUAJIT and require("jit")

if not jit or not LUAJIT or not jit.status() then
  return
end

jit.opt.start(
  "maxtrace=1500",
  "maxrecord=6000",
  "maxirconst=1000",
  "maxmcode=32768"
)

-- ffi overrides for faster rendering calls
local ffi = require("ffi")

local function ffi_error(err)
  error(
    "Error: " .. tostring(err) .. "\n"
    .. debug.traceback("", 4) .. "\n"
  )
end

ffi.cdef [[
  void* ren_get_target_window_ffi(void);
  void rencache_set_clip_rect_ffi(void *window_renderer, float x, float y, float w, float h);
  void rencache_draw_rect_ffi(void *window_renderer, float x, float y, float w, float h, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
  double rencache_draw_text_ffi(void *window_renderer, void **font, const char *text, size_t len, double x, double y, unsigned char r, unsigned char g, unsigned char b, unsigned char a, double tab_offset);
  void rencache_begin_frame_ffi(void *window_renderer);
  void rencache_end_frame_ffi();
  double system_get_time_ffi();
  bool system_wait_event_ffi(double n);
  void system_sleep_ffi(double s);
  bool system_has_pending_events_ffi(void);
]]

renderer.draw_rect_lua = renderer.draw_rect
function renderer.draw_rect(x, y, w, h, color)
  if not color then
    local core = require "core"
    core.error("renderer.draw_rect: color not provided")
    color = {255, 255, 255, 255}
  end
  local win = ffi.C.ren_get_target_window_ffi()
  if win == nil then ffi_error("no target window found") return end
  ffi.C.rencache_draw_rect_ffi(
    win,
    x, y, w, h,
    color[1], color[2], color[3], color[4] or 255
  )
end

renderer.draw_text_lua = renderer.draw_text
local fonts_pointer_cache = setmetatable({}, { __mode = "k" })
function renderer.draw_text(font, text, x, y, color, tab)
  if not color then
    local core = require "core"
    core.error("renderer.draw_text: color not provided")
    color = {255, 255, 255, 255}
  end
  if not fonts_pointer_cache[font] then
    local fonts_list = font
    if type(font) ~= "table" then
      fonts_list = {font}
    end
    local fonts = ffi.new("void*[10]")
    for i, f in pairs(fonts_list) do
      fonts[i-1] = ffi.cast("void**", f)[0]
    end
    fonts[#fonts_list] = nil
    fonts_pointer_cache[font] = fonts
  end
  text = type(text) == "string" and text or tostring(text)
  if not color then color = {255, 255, 255, 255} end
  local win = ffi.C.ren_get_target_window_ffi()
  if win == nil then ffi_error("no target window found") return 0 end
  return ffi.C.rencache_draw_text_ffi(
    win,
    fonts_pointer_cache[font], text, #text, x, y,
    color[1], color[2], color[3], color[4] or 255,
    tab and tab.tab_offset or -1
  );
end

renderer.begin_frame_lua = renderer.begin_frame
local windows_pointer_cache = setmetatable({}, { __mode = "k" })
function renderer.begin_frame(window)
  if not windows_pointer_cache[window] then
    windows_pointer_cache[window] = ffi.cast("void**", window)[0]
  end
  ffi.C.rencache_begin_frame_ffi(windows_pointer_cache[window])
end

renderer.end_frame_lua = renderer.end_frame
function renderer.end_frame()
  ffi.C.rencache_end_frame_ffi()
end

renderer.set_clip_rect_lua = renderer.set_clip_rect
function renderer.set_clip_rect(x, y, w, h)
  local win = ffi.C.ren_get_target_window_ffi()
  if win == nil then ffi_error("no target window found") return end
  ffi.C.rencache_set_clip_rect_ffi(win, x, y, w, h)
end

system.sleep_lua = system.sleep
system.sleep = ffi.C.system_sleep_ffi

system.get_time_lua = system.get_time
system.get_time = ffi.C.system_get_time_ffi

system.get_wait_event_lua = system.wait_event
function system.wait_event(timeout)
  return ffi.C.system_wait_event_ffi(timeout or -1)
end

system.has_pending_events_lua = system.has_pending_events
system.has_pending_events = ffi.C.system_has_pending_events_ffi
