/*
 * qbe_rt.c - Non-inline wrappers for QBE-compiled ƿit modules
 *
 * Provides non-inline versions of static-inline quickjs functions
 * (which QBE-generated code calls as external symbols).
 *
 * All cell_rt_* runtime functions are implemented in source/qbe_helpers.c
 * (compiled into the cell binary) and resolved via -undefined dynamic_lookup.
 */

#include <stdint.h>
#include <string.h>
#include <math.h>

typedef uint64_t JSValue;
typedef struct JSContext JSContext;

#define JS_TAG_SHORT_FLOAT 5
#define JS_TAG_NULL        7
#define JS_VAL_NULL        7

/* ============================================================
   Non-inline wrappers for static-inline quickjs functions
   ============================================================ */

/*
 * __JS_NewFloat64 — encode double as tagged JSValue
 * Short float: [sign:1][exp:8][mantissa:52][tag:3]
 * Returns tagged int if value is an exact integer in int32 range
 */
JSValue __JS_NewFloat64(JSContext *ctx, double d) {
  union { double d; uint64_t u; } u;
  u.d = d;

  uint64_t sign = u.u >> 63;
  int exp = (u.u >> 52) & 0x7FF;
  uint64_t mantissa = u.u & ((1ULL << 52) - 1);

  /* Zero */
  if (exp == 0 && mantissa == 0)
    return JS_TAG_SHORT_FLOAT;
  /* NaN/Inf → null */
  if (exp == 0x7FF)
    return JS_VAL_NULL;
  /* Subnormals → zero */
  if (exp == 0)
    return (sign << 63) | JS_TAG_SHORT_FLOAT;

  int short_exp = exp - 1023 + 127;
  if (short_exp < 1 || short_exp > 254)
    return JS_VAL_NULL;

  /* Prefer integer if exact */
  if (d >= (double)(-2147483647 - 1) && d <= (double)2147483647) {
    int32_t i = (int32_t)d;
    if ((double)i == d)
      return (uint64_t)(uint32_t)i << 1;
  }

  return (sign << 63)
       | ((uint64_t)short_exp << 55)
       | (mantissa << 3)
       | JS_TAG_SHORT_FLOAT;
}

/*
 * JS_IsNumber — check if value is tagged int or short float
 */
int JS_IsNumber(JSValue v) {
  int is_int = (v & 1) == 0;
  int is_float = (v & 7) == JS_TAG_SHORT_FLOAT;
  return is_int || is_float;
}

/*
 * JS_NewString — create string from C string (wraps JS_NewStringLen)
 */
extern JSValue JS_NewStringLen(JSContext *ctx, const char *str, size_t len);

JSValue JS_NewString(JSContext *ctx, const char *str) {
  return JS_NewStringLen(ctx, str, strlen(str));
}
