#include "qjs_staef.h"
#include "font.h"
#include "qjs_macros.h"
#include "HandmadeMath.h"
#include "render.h"
#include "stb_ds.h"
#include "cell.h"
#include "qjs_sdl.h"
#include <SDL3/SDL.h>

// External functions from jsffi.c
typedef HMM_Vec4 colorf;
extern colorf js2color(JSContext *js, JSValue v);
extern JSValue vec22js(JSContext *js, HMM_Vec2 v);
extern HMM_Vec2 js2vec2(JSContext *js, JSValue v);
extern rect js2rect(JSContext *js, JSValue v);
extern double js2number(JSContext *js, JSValue v);
extern JSValue number2js(JSContext *js, double g);
extern void *js_get_blob_data(JSContext *js, size_t *len, JSValue v);
extern JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t size);
extern JSValue quads_to_mesh(JSContext *js, text_vert *buffer);
extern int uncaught_exception(JSContext *js, JSValue ret);

// QuickJS class for font
QJSCLASS(font,)

// Font constructor
JSC_CCALL(staef_font_new,
  size_t len;
  void *data = js_get_blob_data(js, &len, argv[0]);
  if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data");
  
  double height = js2number(js, argv[1]);
  font *f = MakeFont(data, len, (int)height);
  if (!f) return JS_ThrowReferenceError(js, "could not create font");
  
  ret = font2js(js, f);
  
  // Create surface data object for the font's atlas
  if (f->surface) {
    JSValue surfData = JS_NewObject(js);
    JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, f->surface->w));
    JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, f->surface->h));
    JS_SetPropertyStr(js, surfData, "format", pixelformat2js(js, f->surface->format));
    JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, f->surface->pitch));
    
    // Lock surface if needed
    int locked = 0;
    if (SDL_MUSTLOCK(f->surface)) {
      if (SDL_LockSurface(f->surface) < 0)
        return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
      locked = 1;
    }
    
    size_t byte_size = f->surface->pitch * f->surface->h;
    JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, f->surface->pixels, byte_size));
    
    if (locked)
      SDL_UnlockSurface(f->surface);
      
    JS_SetPropertyStr(js, ret, "surface", surfData);
  }
)

// Calculate text size
JSC_CCALL(staef_font_text_size,
  font *f = js2font(js, self);
  const char *str = JS_ToCString(js, argv[0]);
  float letterSpacing = argc > 1 ? js2number(js, argv[1]) : 0;
  float wrap = argc > 2 ? js2number(js, argv[2]) : -1;
  
  ret = vec22js(js, measure_text(str, f, letterSpacing, wrap));
  JS_FreeCString(js, str);
)

// Generate text buffer (mesh data)
JSC_CCALL(staef_font_make_text_buffer,
  font *f = js2font(js, self);
  const char *s = JS_ToCString(js, argv[0]);
  rect rectpos = js2rect(js, argv[1]);
  colorf c = js2color(js, argv[2]);
  float wrap = argc > 3 ? js2number(js, argv[3]) : -1;
  
  HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y };
  text_vert *buffer = renderText(s, startpos, f, c, wrap);
  ret = quads_to_mesh(js, buffer);
  JS_FreeCString(js, s);
  arrfree(buffer);
)

// Font property getters/setters
JSC_GETSET(font, linegap, number)

// Font methods
static const JSCFunctionListEntry js_font_funcs[] = {
  MIST_FUNC_DEF(staef_font, text_size, 3),
  MIST_FUNC_DEF(staef_font, make_text_buffer, 4),
  CGETSET_ADD(font, linegap),
};

// Font constructor function
static JSValue js_font_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
{
  return js_staef_font_new(ctx, JS_NULL, argc, argv);
}

// Initialize the staef module
JSValue js_staef_use(JSContext *js)
{
  JSValue mod = JS_NewObject(js);
  JSValue proto;
  
  // Initialize font class
  JS_NewClassID(&js_font_id);
  JS_NewClass(JS_GetRuntime(js), js_font_id, &js_font_class);
  proto = JS_NewObject(js);
  JS_SetPropertyFunctionList(js, proto, js_font_funcs, countof(js_font_funcs));
  JS_SetClassProto(js, js_font_id, proto);
  
  // Create font constructor
  JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_constructor, 0);
  JS_SetConstructor(js, font_ctor, proto);
  
  // Add font constructor to module (lowercase to match "new staef.font")
  JS_SetPropertyStr(js, mod, "font", font_ctor);
  
  return mod;
}
