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

colorf js2color(JSContext *js,JSValue v) {
  if (JS_IsNull(v)) return (colorf){1,1,1,1};
  
  colorf color = {1,1,1,1}; // Default to white
  
  if (JS_IsArray(v)) {
    // Handle array format: [r, g, b, a]
    JSValue c[4];
    for (int i = 0; i < 4; i++) c[i] = JS_GetPropertyNumber(js,v,i);
    
    color.r = js2number(js,c[0]);
    color.g = js2number(js,c[1]);
    color.b = js2number(js,c[2]);
    color.a = JS_IsNull(c[3]) ? 1.0 : js2number(js,c[3]);
    
    for (int i = 0; i < 4; i++) JS_FreeValue(js,c[i]);
  } else if (JS_IsObject(v)) {
    JS_GETPROP(js, color.r, v, r, number)
    JS_GETPROP(js, color.g, v, g, number)
    JS_GETPROP(js, color.b, v, b, number)
    JS_GETPROP(js, color.a, v, a, number)
  }

  return color;
}

JSValue color2js(JSContext *js, colorf color)
{
  JS_FRAME(js);
  JS_ROOT(arr, JS_NewArray(js));
  JS_SetPropertyNumber(js, arr.val,0,number2js(js,(double)color.r));
  JS_SetPropertyNumber(js, arr.val,1,number2js(js,(double)color.g));
  JS_SetPropertyNumber(js, arr.val,2,number2js(js,(double)color.b));
  JS_SetPropertyNumber(js, arr.val,3,number2js(js,(double)color.a));
  JS_RETURN(arr.val);
}

HMM_Vec2 js2vec2(JSContext *js,JSValue v)
{
  HMM_Vec2 v2;
  
  // Check if it's an array
  if (JS_IsArray(v)) {
    { JSValue val = JS_GetPropertyNumber(js,v,0); v2.X = js2number(js, val); JS_FreeValue(js,val); }
    { JSValue val = JS_GetPropertyNumber(js,v,1); v2.Y = js2number(js, val); JS_FreeValue(js,val); }
  } else {
    // Try to get x,y properties from object
    JSValue x_val = JS_GetPropertyStr(js, v, "x");
    JSValue y_val = JS_GetPropertyStr(js, v, "y");
    
    v2.X = js2number(js, x_val);
    v2.Y = js2number(js, y_val);
    
    JS_FreeValue(js, x_val);
    JS_FreeValue(js, y_val);
  }
  
  return v2;
}

HMM_Vec3 js2vec3(JSContext *js,JSValue v)
{
  HMM_Vec3 v3;
  { JSValue val = JS_GetPropertyNumber(js, v,0); v3.x = js2number(js, val); JS_FreeValue(js,val); }
  { JSValue val = JS_GetPropertyNumber(js, v,1); v3.y = js2number(js, val); JS_FreeValue(js,val); }
  { JSValue val = JS_GetPropertyNumber(js, v,2); v3.z = js2number(js, val); JS_FreeValue(js,val); }
  return v3;
}

float *js2floats(JSContext *js, JSValue v, size_t *len)
{
  *len = JS_ArrayLength(js,v);
  float *arr = malloc(sizeof(float)* *len);
  for (int i = 0; i < *len; i++)
    { JSValue val = JS_GetPropertyNumber(js,v,i); arr[i] = js2number(js, val); JS_FreeValue(js,val); }
  return arr;
}

double *js2doubles(JSContext *js, JSValue v, size_t *len)
{
  *len = JS_ArrayLength(js,v);
  double *arr = malloc(sizeof(double)* *len);
  for (int i = 0; i < *len; i++)
    { JSValue val = JS_GetPropertyNumber(js,v,i); arr[i] = js2number(js, val); JS_FreeValue(js,val); }
  return arr;
}

HMM_Vec3 js2vec3f(JSContext *js, JSValue v)
{
  HMM_Vec3 vec;
  if (JS_IsArray(v))
    return js2vec3(js,v);
  else
    vec.x = vec.y = vec.z = js2number(js,v);
  return vec;
}

JSValue vec32js(JSContext *js, HMM_Vec3 v)
{
  JS_FRAME(js);
  JS_ROOT(array, JS_NewArray(js));
  JS_SetPropertyNumber(js, array.val,0,number2js(js,v.x));
  JS_SetPropertyNumber(js, array.val,1,number2js(js,v.y));
  JS_SetPropertyNumber(js, array.val,2,number2js(js,v.z));
  JS_RETURN(array.val);
}

JSValue vec3f2js(JSContext *js, HMM_Vec3 v)
{
  return vec32js(js,v);
}

JSValue quat2js(JSContext *js, HMM_Quat q)
{
  JS_FRAME(js);
  JS_ROOT(arr, JS_NewArray(js));
  JS_SetPropertyNumber(js, arr.val, 0, number2js(js,q.x));
  JS_SetPropertyNumber(js, arr.val,1,number2js(js,q.y));
  JS_SetPropertyNumber(js, arr.val,2,number2js(js,q.z));
  JS_SetPropertyNumber(js, arr.val,3,number2js(js,q.w));
  JS_RETURN(arr.val);
}

HMM_Vec4 js2vec4(JSContext *js, JSValue v)
{
  HMM_Vec4 v4;
  for (int i = 0; i < 4; i++)
    { JSValue val = JS_GetPropertyNumber(js, v,i); v4.e[i] = js2number(js, val); JS_FreeValue(js,val); }
  return v4;
}

double arr_vec_length(JSContext *js,JSValue v)
{
  int len = JS_ArrayLength(js,v);
  switch(len) {
    case 2: return HMM_LenV2(js2vec2(js,v));
    case 3: return HMM_LenV3(js2vec3(js,v));
    case 4: return HMM_LenV4(js2vec4(js,v));
  }

  double sum = 0;
  for (int i = 0; i < len; i++)
    { JSValue val = JS_GetPropertyNumber(js, v, i); double num = js2number(js, val); JS_FreeValue(js,val); sum += pow(num, 2); }

  return sqrt(sum);
}

HMM_Quat js2quat(JSContext *js,JSValue v)
{
  return js2vec4(js,v).quat;
}

JSValue vec42js(JSContext *js, HMM_Vec4 v)
{
  JS_FRAME(js);
  JS_ROOT(array, JS_NewArray(js));
  for (int i = 0; i < 4; i++)
    JS_SetPropertyNumber(js, array.val,i,number2js(js,v.e[i]));
  JS_RETURN(array.val);
}

HMM_Vec2 *js2cpvec2arr(JSContext *js,JSValue v) {
  HMM_Vec2 *arr = NULL;
  int n = JS_ArrayLength(js,v);
  arrsetlen(arr,n);
  
  for (int i = 0; i < n; i++) {
    JSValue ii = JS_GetPropertyNumber(js,v,i);
    arr[i] = js2vec2(js,ii);
    JS_FreeValue(js,ii);
  }
  
  return arr;
}

rect js2rect(JSContext *js,JSValue v) {
  if (JS_IsNull(v)) return (rect){0,0,1,1};
  rect rect;
  JS_GETATOM(js,rect.x,v,x,number)
  JS_GETATOM(js,rect.y,v,y,number)
  JS_GETATOM(js,rect.w,v,width,number)
  JS_GETATOM(js,rect.h,v,height,number)
  float anchor_x, anchor_y;
  JS_GETATOM(js, anchor_x, v, anchor_x, number)
  JS_GETATOM(js, anchor_y, v, anchor_y, number)

  rect.y -= anchor_y*rect.h;
  rect.x -= anchor_x*rect.w;

  return rect;
}

static JSValue floats2array(JSContext *js, float *vals, size_t len) {
  JS_FRAME(js);
  JS_ROOT(arr, JS_NewArray(js));
  for (size_t i = 0; i < len; i++) {
    JS_SetPropertyNumber(js, arr.val, i, number2js(js, vals[i]));
  }
  JS_RETURN(arr.val);
}

lrtb js2lrtb(JSContext *js, JSValue v)
{
  lrtb ret = {0};
  JS_GETATOM(js,ret.l,v,l,number)
  JS_GETATOM(js,ret.r,v,r,number)
  JS_GETATOM(js,ret.b,v,b,number)
  JS_GETATOM(js,ret.t,v,t,number)
  return ret;
}

JSValue vec22js(JSContext *js,HMM_Vec2 v)
{
  JS_FRAME(js);
  JS_ROOT(array, JS_NewArray(js));
  JS_SetPropertyNumber(js, array.val,0,number2js(js,v.x));
  JS_SetPropertyNumber(js, array.val,1,number2js(js,v.y));
  JS_RETURN(array.val);
}

JSValue vecarr2js(JSContext *js,HMM_Vec2 *points, int n) {
  JS_FRAME(js);
  JS_ROOT(array, JS_NewArray(js));
  for (int i = 0; i < n; i++)
    JS_SetPropertyNumber(js, array.val,i,vec22js(js,points[i]));
  JS_RETURN(array.val);
}

JSValue rect2js(JSContext *js,rect rect) {
  JS_FRAME(js);
  JS_ROOT(obj, JS_NewObject(js));
  JS_SetPropertyStr(js, obj.val, "x", number2js(js, rect.x));
  JS_SetPropertyStr(js, obj.val, "y", number2js(js, rect.y));
  JS_SetPropertyStr(js, obj.val, "width", number2js(js, rect.w));
  JS_SetPropertyStr(js, obj.val, "height", number2js(js, rect.h));
  JS_RETURN(obj.val);
}

float *rgba2floats(float *r, struct rgba c)
{
  r[0] = (float)c.r / RGBA_MAX;
  r[1] = (float)c.g / RGBA_MAX;
  r[2] = (float)c.b / RGBA_MAX;
  r[3] = (float)c.a / RGBA_MAX;
  return r;
}

JSValue angle2js(JSContext *js, double angle) {
  return number2js(js, angle);
}

double js2angle(JSContext *js, JSValue v) {
  return js2number(js, v);
}
