#include "prosperon.h"
#include "cell.h"

#include "HandmadeMath.h"

struct sprite{
  HMM_Vec2 pos; // x,y coordinates of the sprite
  HMM_Vec2 center;
  HMM_Vec2 skew;
  HMM_Vec2 scale;
  float rotation;
  HMM_Mat2 affine;
  JSValue image; // the JS image
  int layer; // layer this sprite draws onto
  HMM_Vec4 color; // color in 0-1f
};

typedef struct sprite sprite;

sprite *make_sprite(void)
{
  sprite *sprite = calloc(sizeof(*sprite),1);
  sprite->image = JS_NULL;
  return sprite;
}

void sprite_free(JSRuntime *rt, sprite *sprite)
{
  free(sprite);
}

void sprite_apply(sprite *sp)
{
  /* -------- rotation + skew → 2×2 affine matrix -------- */
  float  rot = sp->rotation;
  HMM_Vec2 k = sp->skew;

  float c = cosf(rot), s = sinf(rot);

  sp->affine.Columns[0] = (HMM_Vec2){ .x =  c + k.x * s,
                                      .y =  k.y * c + s };
  sp->affine.Columns[1] = (HMM_Vec2){ .x = -s + k.x * c,
                                      .y = -k.y * s + c };
}

// Sprite class definitions
static JSClassID js_sprite_id;

QJSCLASSMARK(sprite,)

// SPRITE ACTION FUNCTIONS

JSC_CCALL(sprite_move,
  sprite *sp = js2sprite(js,self);
  HMM_Vec2 mv = js2vec2(js,argv[0]);
  sp->pos.x += mv.x;
  sp->pos.y += mv.y;
)

JSC_CCALL(sprite_set_affine,
  sprite *sp = js2sprite(js,self);
  sprite_apply(sp);
)

JSC_CCALL(sprite_set_image,
  sprite *sp = js2sprite(js,self);
  sp->image = argv[0];
)

JSC_CCALL(sprite_set,
  sprite *sp = js2sprite(js,self);
  if (!JS_IsObject(argv[0])) return JS_NULL;
  
  JSValue val;
  
  val = JS_GetPropertyStr(js, argv[0], "pos");
  if (!JS_IsNull(val)) { sp->pos = js2vec2(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "center");
  if (!JS_IsNull(val)) { sp->center = js2vec2(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "skew");
  if (!JS_IsNull(val)) { sp->skew = js2vec2(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "scale");
  if (!JS_IsNull(val)) { sp->scale = js2vec2(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "rotation");
  if (!JS_IsNull(val)) { sp->rotation = js2number(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "layer");
  if (!JS_IsNull(val)) { sp->layer = js2number(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "color");
  if (!JS_IsNull(val)) { sp->color = js2color(js, val); }
  
  val = JS_GetPropertyStr(js, argv[0], "image");
  if (!JS_IsNull(val) && !JS_IsNull(val)) {
    sp->image = val;  // Transfer ownership
  }
)


static const JSCFunctionListEntry js_sprite_funcs[] = {
  MIST_FUNC_DEF(sprite, set_affine, 0),
  MIST_FUNC_DEF(sprite, set_image, 1),
  MIST_FUNC_DEF(sprite, move, 1),
  MIST_FUNC_DEF(sprite, set, 1),
};

// Constructor function for sprite
static JSValue js_sprite_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv) {
  sprite *sp = make_sprite();
  if (!sp) return JS_ThrowOutOfMemory(js);
  
  // If an options object is provided, initialize the sprite with it
  if (argc > 0 && JS_IsObject(argv[0])) {
    JS_GETATOM(js, sp->pos, argv[0], pos, vec2)
    JS_GETATOM(js, sp->center, argv[0], center, vec2)
    JS_GETATOM(js, sp->skew, argv[0], skew, vec2)
    JS_GETATOM(js, sp->scale, argv[0], scale, vec2)
    JS_GETATOM(js, sp->rotation, argv[0], rotation, number)
    JS_GETATOM(js, sp->layer, argv[0], layer, number)
    JS_GETATOM(js, sp->color, argv[0], color, color)
    
    JSValue image = JS_GetPropertyStr(js, argv[0], "image");
    if (!JS_IsNull(image)) {
      sp->image = image;  // Transfer ownership, no need to dup
    }
  }
  
  // Default values if not provided
  if (sp->scale.x == 0 && sp->scale.y == 0) {
    sp->scale.x = 1;
    sp->scale.y = 1;
  }
  if (sp->color.w == 0) sp->color.w = 1;  // Default alpha to 1
  
  sprite_apply(sp);
  return sprite2js(js, sp);
}

CELL_USE_INIT(
  QJSCLASSPREP_FUNCS(sprite);
  JSValue ctor = JS_NewCFunction2(js, js_sprite_constructor, "sprite", 1, JS_CFUNC_generic, 0);
  return ctor;
)