/*
 * QuickJS Javascript Engine
 *
 * Copyright (c) 2017-2021 Fabrice Bellard
 * Copyright (c) 2017-2021 Charlie Gordon
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */
#ifndef QUICKJS_H
#define QUICKJS_H

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

#ifdef __cplusplus
extern "C" {
#endif

#if defined(__GNUC__) || defined(__clang__)
#define __js_printf_like(f, a) __attribute__ ((format (printf, f, a)))
#else
#define __js_printf_like(a, b)
#endif

// #define BINARY16    // 16 bit word type (half)
// #define BINARY32    // 32 bit word type (float)
// #define BINARY64    // 64 bit word type (double)
// #define DEC64       // 64 bit word type (dec)

enum mist_obj_type {
  OBJ_ARRAY = 0,
  OBJ_BLOB = 1,
  OBJ_TEXT = 2,
  OBJ_RECORD = 3, // js style objects
  OBJ_FUNCTION = 4,
  OBJ_CODE = 5,
  OBJ_FRAME = 6,
  OBJ_FORWARD = 7
};

typedef uint64_t JSValue;

#define OBJHDR_S_BIT 3u
#define OBJHDR_P_BIT 4u
#define OBJHDR_A_BIT 5u
#define OBJHDR_R_BIT 7u

#define OBJHDR_FLAG(bit) ((objhdr_t)1ull << (bit))
#define OBJHDR_S_MASK OBJHDR_FLAG (OBJHDR_S_BIT)
#define OBJHDR_P_MASK OBJHDR_FLAG (OBJHDR_P_BIT)
#define OBJHDR_A_MASK OBJHDR_FLAG (OBJHDR_A_BIT)
#define OBJHDR_R_MASK OBJHDR_FLAG (OBJHDR_R_BIT)

typedef uint64_t word_t;   // one actor-memory word
typedef uint64_t objhdr_t; // header word
typedef uint64_t objref_t; // 56-bit word address (0 = null)

static inline uint8_t objhdr_type (objhdr_t h) { return (uint8_t)(h & 7u); }
static inline int objhdr_s (objhdr_t h) { return (h & OBJHDR_S_MASK) != 0; }

/*
  Half: 10 bits
  Float: 23 bits
  Double: 52 bits
  Dec16: 11 bits
  Dec32: 26 bits
  Dec64: 56 bits

  On double builds, 48 bits
  On Dec64, max
*/

#define JS_BOOL int

typedef struct JSRuntime JSRuntime; // the entire VM
typedef struct JSContext JSContext; // Each actor
typedef struct JSClass JSClass;
typedef uint32_t JSClassID;

/* Forward declaration - JSGCRef moved after JSValue definition */
struct JSGCRef;

/* ============================================================
   Mist Value Encoding (LSB-based type discrimination)
   ============================================================

   64-bit builds:
     LSB = 0        → 31-bit signed integer (value >> 1)
     LSB = 001       → 61-bit pointer (aligned, clear low 3 bits)
     LSB = 101      → Short float (61-bit, 3 fewer exponent bits than double)
     LSB = 11       → Special tag (next 3 bits = subtype, 5 bits total)

   32-bit builds:
     LSB = 0        → 31-bit signed integer
     LSB = 01       → 30-bit pointer
     LSB = 11       → Special tag (next 3 bits = subtype)

   ============================================================ */

/* JSValueConst is just JSValue (const is not needed in value semantics) */
typedef JSValue JSValueConst;

#define JSW 8

/* LSB-based tags */
enum {
  /* Primary tags (low bits) */
  JS_TAG_INT = 0, /* LSB = 0 */
  JS_TAG_PTR = 1, /* LSB = 01 */
  JS_TAG_SHORT_FLOAT = 5, /* LSB = 101 */
  JS_TAG_SPECIAL = 3, /* LSB = 11 */

  /* Special subtypes (5 bits: xxxx11) */
  JS_TAG_BOOL = 0x03,          /* 00011 */
  JS_TAG_NULL = 0x07,          /* 00111 */
  JS_TAG_EXCEPTION = 0x0F,     /* 01111 */
  JS_TAG_STRING_IMM = 0x0B,    /* 01011 - immediate ASCII (up to 7 chars) */
};

/* Compatibility tag aliases for external code */
#define JS_TAG_STRING JS_TAG_STRING_IMM  /* Alias: text/string type */
#define JS_TAG_FLOAT64 JS_TAG_SHORT_FLOAT /* Alias: floats use short float encoding */

#define JS_EMPTY_TEXT ((JSValue)JS_TAG_STRING_IMM)

/* JSGCRef - for rooting values during GC */
typedef struct JSGCRef {
    JSValue val;
    struct JSGCRef *prev;
} JSGCRef;

/* JSLocalRef - GC updates C locals through pointers (OCaml-style) */
typedef struct JSLocalRef {
    JSValue *ptr;
    struct JSLocalRef *prev;
} JSLocalRef;

/* stack of JSGCRef */
JSValue *JS_PushGCRef(JSContext *ctx, JSGCRef *ref);
JSValue JS_PopGCRef(JSContext *ctx, JSGCRef *ref);

/* JS_FRAME/JS_ROOT/JS_LOCAL helpers (for use from cell.h macros) */
JSGCRef *JS_GetGCFrame(JSContext *ctx);
JSLocalRef *JS_GetLocalFrame(JSContext *ctx);
void JS_PushLocalRef(JSContext *ctx, JSLocalRef *ref);
void JS_RestoreFrame(JSContext *ctx, JSGCRef *gc_frame, JSLocalRef *local_frame);

#define JS_PUSH_VALUE(ctx, v) do { JS_PushGCRef(ctx, &v ## _ref); v ## _ref.val = v; } while (0)
#define JS_POP_VALUE(ctx, v) v = JS_PopGCRef(ctx, &v ## _ref)

/* list of JSGCRef (they can be removed in any order, slower) */
JSValue *JS_AddGCRef(JSContext *ctx, JSGCRef *ref);
void JS_DeleteGCRef(JSContext *ctx, JSGCRef *ref);

/* ============================================================
   Value Extraction
   ============================================================ */

/* Get primary tag (low 2-3 bits) */
static inline int
JS_VALUE_GET_TAG (JSValue v) {
  if ((v & 1) == 0) return JS_TAG_INT;
  if ((v & 7) == JS_TAG_SHORT_FLOAT) return JS_TAG_SHORT_FLOAT;
  if ((v & 3) == JS_TAG_PTR) return JS_TAG_PTR;
  return (int)(v & 0x1F); /* special tag */
}

#define JS_VALUE_GET_NORM_TAG(v) JS_VALUE_GET_TAG (v)

/* Extract 31-bit signed int (when LSB=0) */
#define JS_VALUE_GET_INT(v) ((int32_t)((int64_t)(v) >> 1))

/* Extract bool value from special tag */
#define JS_VALUE_GET_BOOL(v) ((int)((v) >> 5) & 1)

/* Extract special tag payload (bits 5+) */
#define JS_VALUE_GET_SPECIAL_PAYLOAD(v) ((int32_t)((v) >> 5))

/* ============================================================
   Value Creation
   ============================================================ */

#define JS_MKVAL(tag, val) _JS_MkVal (tag, val)
static inline JSValue _JS_MkVal (int tag, int32_t val) {
  if (tag == JS_TAG_INT) { return ((JSValue)(uint32_t)val << 1); }
  /* Special tags encode payload in upper bits */
  return ((JSValue)tag) | ((JSValue)(uint32_t)val << 5);
}

/* ============================================================
   Short Float (64-bit only)
   Range: ~+-3.4e38 (8-bit exponent vs double's 11-bit)
   Out of range → JS_NULL
   ============================================================ */

static inline 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 → short float zero (always +0, no -0) */
  if (exp == 0 && mantissa == 0) { return JS_TAG_SHORT_FLOAT; }

  /* NaN/Inf → NULL */
  if (exp == 0x7FF) { return JS_MKVAL (JS_TAG_NULL, 0); }

  /* Subnormals → zero */
  if (exp == 0) { return (sign << 63) | JS_TAG_SHORT_FLOAT; }

  /* Convert exponent: double bias 1023 → short bias 127 */
  int short_exp = exp - 1023 + 127;
  if (short_exp < 1 || short_exp > 254) {
    return JS_MKVAL (JS_TAG_NULL, 0); /* out of range */
  }

  /* Prefer integer if exact */
  if (d >= INT32_MIN && d <= INT32_MAX) {
    int32_t i = (int32_t)d;
    if ((double)i == d) { return JS_MKVAL (JS_TAG_INT, i); }
  }

  /* Encode: [sign:1][exp:8][mantissa:52][tag:3] */
  return (sign << 63) | ((uint64_t)short_exp << 55) | (mantissa << 3)
         | JS_TAG_SHORT_FLOAT;
}

static inline double JS_VALUE_GET_FLOAT64 (JSValue v) {
  if ((v & 7) != JS_TAG_SHORT_FLOAT) return 0.0;

  uint64_t sign = v >> 63;
  uint64_t short_exp = (v >> 55) & 0xFF;
  uint64_t mantissa = (v >> 3) & ((1ULL << 52) - 1);

  if (short_exp == 0) return 0.0;  /* Always +0, no -0 */

  uint64_t exp = short_exp - 127 + 1023;
  union {
    double d;
    uint64_t u;
  } u;
  u.u = (sign << 63) | (exp << 52) | mantissa;
  return u.d;
}

#define JS_TAG_IS_FLOAT64(tag) ((tag) == JS_TAG_SHORT_FLOAT)
#define JS_NAN JS_MKVAL (JS_TAG_NULL, 0)

/* ============================================================
   Type Checks
   ============================================================ */

static inline JS_BOOL JS_IsInt (JSValue v) { return (v & 1) == 0; }
static inline JS_BOOL JS_IsPtr (JSValue v) { return (v & 7) == JS_TAG_PTR; }
static inline JS_BOOL JS_IsSpecial (JSValue v) { return (v & 3) == JS_TAG_SPECIAL; }

static inline JS_BOOL
JS_IsShortFloat (JSValue v) {
  return (v & 7) == JS_TAG_SHORT_FLOAT;
}

#define JS_VALUE_IS_BOTH_INT(v1, v2) (((v1) & 1) == 0 && ((v2) & 1) == 0)
#define JS_VALUE_IS_BOTH_FLOAT(v1, v2)                                        \
  (JS_IsShortFloat (v1) && JS_IsShortFloat (v2))

/* ============================================================
   Special Constants
   ============================================================ */

#define JS_NULL ((JSValue)JS_TAG_NULL)
#define JS_FALSE ((JSValue)JS_TAG_BOOL)
#define JS_TRUE ((JSValue)(JS_TAG_BOOL | (1 << 5)))
#define JS_EXCEPTION ((JSValue)JS_TAG_EXCEPTION)
#define JS_TAG_SUSPENDED 0x13     /* 10011 - distinct special tag */
#define JS_SUSPENDED ((JSValue)JS_TAG_SUSPENDED)

static inline JS_BOOL JS_IsSuspended(JSValue v) {
  return JS_VALUE_GET_TAG(v) == JS_TAG_SUSPENDED;
}

#ifndef JS_DEFAULT_STACK_SIZE
#define JS_DEFAULT_STACK_SIZE (1024 * 1024)
#endif

/* Internal compile flags */
#define JS_EVAL_FLAG_COMPILE_ONLY (1 << 5)      /* internal use */

typedef JSValue JSCFunction (JSContext *ctx, JSValue this_val, int argc,
                             JSValue *argv);
typedef JSValue JSCFunctionMagic (JSContext *ctx, JSValue this_val,
                                  int argc, JSValue *argv, int magic);
typedef JSValue JSCFunctionData (JSContext *ctx, JSValue this_val,
                                 int argc, JSValue *argv, int magic,
                                 JSValue *data);

/* ============================================================
   1. Runtime / Context Lifecycle
   ============================================================ */

JSRuntime *JS_NewRuntime (void);
void JS_FreeRuntime (JSRuntime *rt);
void JS_SetMemoryLimit (JSRuntime *rt, size_t limit);
void JS_SetPoolSize (JSRuntime *rt, size_t initial, size_t cap);

JSContext *JS_NewContext (JSRuntime *rt);
JSContext *JS_NewContextWithHeapSize (JSRuntime *rt, size_t heap_size);
void JS_FreeContext (JSContext *s);
void *JS_GetContextOpaque (JSContext *ctx);
void JS_SetContextOpaque (JSContext *ctx, void *opaque);
void JS_SetActorSym (JSContext *ctx, JSValue sym);
JSValue JS_GetActorSym (JSContext *ctx);
JSRuntime *JS_GetRuntime (JSContext *ctx);
/* use 0 to disable maximum stack size check */
void JS_SetMaxStackSize (JSContext *ctx, size_t stack_size);
/* should be called when changing thread to update the stack top value
   used to check stack overflow. */
void JS_UpdateStackTop (JSContext *ctx);

/* Returns the current VM call depth (0 = pure bytecode, >0 = C frames) */
int JS_GetVMCallDepth(JSContext *ctx);

/* Set per-context heap memory limit (0 = no limit) */
void JS_SetHeapMemoryLimit(JSContext *ctx, size_t limit);

/* Set the pause flag on a context (0=normal, 1=suspend, 2=kill) */
void JS_SetPauseFlag(JSContext *ctx, int value);

JS_BOOL JS_IsLiveObject (JSRuntime *rt, JSValue obj);

/* Class system */
typedef void JSClassFinalizer (JSRuntime *rt, JSValue val);
typedef JSValue JSClassCall (JSContext *ctx, JSValue func_obj,
                             JSValue this_val, int argc,
                             JSValue *argv, int flags);

typedef struct JSClassDef {
  const char *class_name;
  JSClassFinalizer *finalizer;
  JSClassCall *call;
} JSClassDef;

#define JS_INVALID_CLASS_ID 0
extern JSClassID js_class_id_alloc;
JSClassID JS_NewClassID (JSClassID *pclass_id);
/* Returns the class ID if `v` is an object, otherwise returns
 * JS_INVALID_CLASS_ID. */
JSClassID JS_GetClassID (JSValue v);
int JS_NewClass (JSContext *ctx, JSClassID class_id,
                 const JSClassDef *class_def);
int JS_IsRegisteredClass (JSContext *ctx, JSClassID class_id);
void JS_SetClassProto (JSContext *ctx, JSClassID class_id, JSValue obj);
JSValue JS_GetClassProto (JSContext *ctx, JSClassID class_id);

/* ============================================================
   2. Value Creation and Type Checks
   ============================================================ */

static inline JSValue
JS_NewBool (JSContext *ctx, JS_BOOL val) {
  return JS_MKVAL (JS_TAG_BOOL, (val != 0));
}

static inline JSValue
JS_NewInt32 (JSContext *ctx, int32_t val) {
  return JS_MKVAL (JS_TAG_INT, val);
}

static inline JSValue
JS_NewInt64 (JSContext *ctx, int64_t val) {
  JSValue v;
  if (val == (int32_t)val) {
    v = JS_NewInt32 (ctx, val);
  } else {
    v = __JS_NewFloat64 (ctx, val);
  }
  return v;
}

static inline JSValue
JS_NewUint32 (JSContext *ctx, uint32_t val) {
  JSValue v;
  if (val <= 0x7fffffff) {
    v = JS_NewInt32 (ctx, val);
  } else {
    v = __JS_NewFloat64 (ctx, val);
  }
  return v;
}

static inline JSValue
JS_NewFloat64 (JSContext *ctx, double d) {
  int32_t val;
  union {
    double d;
    uint64_t u;
  } u, t;
  if (d >= INT32_MIN && d <= INT32_MAX) {
    u.d = d;
    val = (int32_t)d;
    t.d = val;
    /* -0 cannot be represented as integer, so we compare the bit
       representation */
    if (u.u == t.u) return JS_MKVAL (JS_TAG_INT, val);
  }
  return __JS_NewFloat64 (ctx, d);
}

/* Inline type checks (immediate tags) */
static inline JS_BOOL JS_IsNumber (JSValue v) {
  int tag = JS_VALUE_GET_TAG (v);
  return tag == JS_TAG_INT || JS_TAG_IS_FLOAT64 (tag);
}

static inline JS_BOOL JS_IsBool (JSValue v) {
  return JS_VALUE_GET_TAG (v) == JS_TAG_BOOL;
}

static inline JS_BOOL JS_IsNull (JSValue v) {
  return JS_VALUE_GET_TAG (v) == JS_TAG_NULL;
}

static inline JS_BOOL JS_IsException (JSValue v) {
  return (JS_VALUE_GET_TAG (v) == JS_TAG_EXCEPTION);
}

/* Immediate ASCII string helpers */
#define MIST_ASCII_MAX_LEN 7

static inline JS_BOOL
MIST_IsImmediateASCII (JSValue v) {
  return (v & 0x1F) == JS_TAG_STRING_IMM;
}

static inline int
MIST_GetImmediateASCIILen (JSValue v) {
  return (int)((v >> 5) & 0x7);
}

static inline int MIST_GetImmediateASCIIChar (JSValue v, int idx) {
  return (int)((v >> (8 + idx * 8)) & 0xFF);
}

static inline JSValue MIST_TryNewImmediateASCII (const char *str, size_t len) {
  if (len > MIST_ASCII_MAX_LEN) return JS_NULL;
  JSValue v = (JSValue)JS_TAG_STRING_IMM | ((JSValue)len << 5);
  for (size_t i = 0; i < len; i++) {
    uint8_t c = (uint8_t)str[i];
    if (c >= 0x80) return JS_NULL;
    v |= (JSValue)c << (8 + i * 8);
  }
  return v;
}

/* Heap object type checks (non-inline — see mist_is_* in quickjs-internal.h
   for inline versions used by the VM dispatch loop) */
JS_BOOL JS_IsArray(JSValue v);
JS_BOOL JS_IsRecord(JSValue v);
#define JS_IsObject JS_IsRecord
JS_BOOL JS_IsFunction(JSValue v);
JS_BOOL JS_IsBlob(JSValue v);
JS_BOOL JS_IsText(JSValue v);
JS_BOOL JS_IsStone(JSValue v);

/* ============================================================
   3. GC References
   ============================================================
   With a copying GC, reference counting is not needed since all live
   objects are discovered by tracing from roots. These macros make
   existing DupValue/FreeValue calls into no-ops.
   ============================================================ */
#define JS_DupValue(ctx, v) (v)
#define JS_FreeValue(ctx, v) ((void)0)
#define JS_DupValueRT(rt, v) (v)
#define JS_FreeValueRT(rt, v) ((void)0)

/* ============================================================
   4. Property Access
   ============================================================ */

JSValue JS_GetProperty (JSContext *ctx, JSValue this_obj, JSValue prop);
int JS_SetProperty (JSContext *ctx, JSValue this_obj, JSValue prop, JSValue val);

JSValue JS_GetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop);
int JS_SetPropertyStr (JSContext *ctx, JSValue this_obj, const char *prop, JSValue val);

JSValue JS_GetPropertyNumber (JSContext *ctx, JSValue this_obj, int idx);
JSValue JS_SetPropertyNumber (JSContext *ctx, JSValue obj, int idx, JSValue val);

JSValue JS_GetPrototype (JSContext *ctx, JSValue val);
JSValue JS_GetOwnPropertyNames (JSContext *ctx, JSValue obj);
int JS_GetLength (JSContext *ctx, JSValue obj, int64_t *pres);

void JS_SetOpaque (JSValue obj, void *opaque);
void *JS_GetOpaque (JSValue obj, JSClassID class_id);
void *JS_GetOpaque2 (JSContext *ctx, JSValue obj, JSClassID class_id);
void *JS_GetAnyOpaque (JSValue obj, JSClassID *class_id);

/* ============================================================
   5. Object / Array / String Creation
   ============================================================ */

JSValue JS_NewObjectProtoClass (JSContext *ctx, JSValue proto, JSClassID class_id);
JSValue JS_NewObjectClass (JSContext *ctx, int class_id);
JSValue JS_NewObjectProto (JSContext *ctx, JSValue proto);
JSValue JS_NewObject (JSContext *ctx);
JSValue JS_NewObjectCap (JSContext *ctx, uint32_t n);

JSValue JS_NewArray (JSContext *ctx);
JSValue JS_NewArrayLen (JSContext *ctx, uint32_t len);
JSValue JS_NewArrayCap (JSContext *ctx, uint32_t cap);
JSValue JS_NewArrayFrom (JSContext *ctx, int count, JSValue *values);
int JS_ArrayPush (JSContext *ctx, JSValue *arr_ptr, JSValue val);
JSValue JS_ArrayPop (JSContext *ctx, JSValue obj);

JSValue JS_NewStringLen (JSContext *ctx, const char *str1, size_t len1);
static inline JSValue JS_NewString (JSContext *ctx, const char *str) {
  return JS_NewStringLen (ctx, str, strlen (str));
}

/* ============================================================
   6. Type Conversion
   ============================================================ */

int JS_ToBool (JSContext *ctx, JSValue val); /* return -1 for JS_EXCEPTION */
int JS_ToInt32 (JSContext *ctx, int32_t *pres, JSValue val);
static inline int JS_ToUint32 (JSContext *ctx, uint32_t *pres, JSValue val) {
  return JS_ToInt32 (ctx, (int32_t *)pres, val);
}
int JS_ToInt64 (JSContext *ctx, int64_t *pres, JSValue val);
int JS_ToFloat64 (JSContext *ctx, double *pres, JSValue val);

JSValue JS_ToString (JSContext *ctx, JSValue val);
JSValue JS_ToPropertyKey (JSContext *ctx, JSValue val);

const char *JS_ToCStringLen2 (JSContext *ctx, size_t *plen, JSValue val1, JS_BOOL cesu8);
static inline const char * JS_ToCStringLen (JSContext *ctx, size_t *plen, JSValue val1) {
  return JS_ToCStringLen2 (ctx, plen, val1, 0);
}
static inline const char * JS_ToCString (JSContext *ctx, JSValue val1) {
  return JS_ToCStringLen2 (ctx, NULL, val1, 0);
}
void JS_FreeCString (JSContext *ctx, const char *ptr);

JS_BOOL JS_StrictEq (JSContext *ctx, JSValue op1, JSValue op2);

/* ============================================================
   7. Error Handling
   ============================================================ */

JSValue JS_Throw (JSContext *ctx, JSValue obj);
JSValue JS_GetException (JSContext *ctx);
JS_BOOL JS_HasException (JSContext *ctx);
JSValue __js_printf_like (2, 3)
  JS_ThrowSyntaxError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
  JS_ThrowTypeError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
  JS_ThrowReferenceError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
  JS_ThrowRangeError (JSContext *ctx, const char *fmt, ...);
JSValue __js_printf_like (2, 3)
  JS_ThrowInternalError (JSContext *ctx, const char *fmt, ...);
JSValue JS_ThrowOutOfMemory (JSContext *ctx);

/* ============================================================
   8. Function Creation and Invocation
   ============================================================ */

JSValue JS_Call (JSContext *ctx, JSValue func_obj, JSValue this_obj, int argc, JSValue *argv);
JSValue JS_Stone (JSContext *ctx, JSValue this_val);

/* JSON */
/* 'buf' must be zero terminated i.e. buf[buf_len] = '\0'. */
JSValue JS_ParseJSON (JSContext *ctx, const char *buf, size_t buf_len,
                      const char *filename);
#define JS_PARSE_JSON_EXT (1 << 0) /* allow extended JSON */
JSValue JS_ParseJSON2 (JSContext *ctx, const char *buf, size_t buf_len,
                       const char *filename, int flags);
JSValue JS_JSONStringify (JSContext *ctx, JSValue obj,
                          JSValue replacer, JSValue space0,
                          JS_BOOL compact_arrays);

/* ============================================================
   9. Intrinsic Wrappers (JS_Cell* / JS_Array*)
   ============================================================ */

/* Intrinsic array operations */
JSValue JS_Array (JSContext *ctx, JSValue arg0, JSValue arg1, JSValue arg2, JSValue arg3);
JSValue JS_ArrayFilter (JSContext *ctx, JSValue arr, JSValue fn);
JSValue JS_ArraySort (JSContext *ctx, JSValue arr, JSValue selector);
JSValue JS_ArrayFind (JSContext *ctx, JSValue arr, JSValue target_or_fn, JSValue reverse, JSValue from);
JSValue JS_ArrFor (JSContext *ctx, JSValue arr, JSValue fn, JSValue reverse, JSValue exit_val);
JSValue JS_ArrayReduce (JSContext *ctx, JSValue arr, JSValue fn, JSValue initial, JSValue reverse);

/* Core cell functions */
JSValue JS_CellStone (JSContext *ctx, JSValue val);
JSValue JS_CellLength (JSContext *ctx, JSValue val);
JSValue JS_CellReverse (JSContext *ctx, JSValue val);
JSValue JS_CellProto (JSContext *ctx, JSValue obj);
JSValue JS_CellSplat (JSContext *ctx, JSValue val);
JSValue JS_CellMeme (JSContext *ctx, JSValue obj, JSValue deep);
JSValue JS_CellApply (JSContext *ctx, JSValue fn, JSValue args);
JSValue JS_CellCall (JSContext *ctx, JSValue fn, JSValue this_val, JSValue args);
JSValue JS_CellModulo (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellNeg (JSContext *ctx, JSValue val);
JSValue JS_CellNot (JSContext *ctx, JSValue val);

/* Text cell functions */
JSValue JS_CellText (JSContext *ctx, JSValue val);
JSValue JS_CellLower (JSContext *ctx, JSValue text);
JSValue JS_CellUpper (JSContext *ctx, JSValue text);
JSValue JS_CellTrim (JSContext *ctx, JSValue text, JSValue chars);
JSValue JS_CellCodepoint (JSContext *ctx, JSValue text, JSValue idx);
JSValue JS_CellReplace (JSContext *ctx, JSValue text, JSValue pattern, JSValue replacement);
JSValue JS_CellSearch (JSContext *ctx, JSValue text, JSValue pattern, JSValue from);
JSValue JS_CellExtract (JSContext *ctx, JSValue text, JSValue from, JSValue to);
JSValue JS_CellCharacter (JSContext *ctx, JSValue codepoint);

/* Number cell functions */
JSValue JS_CellNumber (JSContext *ctx, JSValue val);
JSValue JS_CellAbs (JSContext *ctx, JSValue num);
JSValue JS_CellSign (JSContext *ctx, JSValue num);
JSValue JS_CellFloor (JSContext *ctx, JSValue num);
JSValue JS_CellCeiling (JSContext *ctx, JSValue num);
JSValue JS_CellRound (JSContext *ctx, JSValue num);
JSValue JS_CellTrunc (JSContext *ctx, JSValue num);
JSValue JS_CellWhole (JSContext *ctx, JSValue num);
JSValue JS_CellFraction (JSContext *ctx, JSValue num);
JSValue JS_CellMin (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellMax (JSContext *ctx, JSValue a, JSValue b);
JSValue JS_CellRemainder (JSContext *ctx, JSValue a, JSValue b);

/* Object cell functions */
JSValue JS_CellObject (JSContext *ctx, JSValue proto, JSValue props);

/* Format */
JSValue JS_CellFormat (JSContext *ctx, JSValue text, JSValue collection, JSValue transformer);

/* Output helpers */
void JS_PrintText (JSContext *ctx, JSValue val);
void JS_PrintTextLn (JSContext *ctx, JSValue val);
void JS_PrintFormatted (JSContext *ctx, const char *fmt, int count, JSValue *values);

/* ============================================================
   10. C Function Definition
   ============================================================ */
typedef enum JSCFunctionEnum {
  JS_CFUNC_generic,
  JS_CFUNC_generic_magic,
  JS_CFUNC_f_f,
  JS_CFUNC_f_f_f,
  /* Fixed-arity fast paths - no argc/argv overhead */
  JS_CFUNC_0, /* JSValue f(ctx, this_val) */
  JS_CFUNC_1, /* JSValue f(ctx, this_val, arg0) */
  JS_CFUNC_2, /* JSValue f(ctx, this_val, arg0, arg1) */
  JS_CFUNC_3, /* JSValue f(ctx, this_val, arg0, arg1, arg2) */
  JS_CFUNC_4,
  /* Pure functions (no this_val) - for global utility functions */
  JS_CFUNC_PURE,    /* JSValue f(ctx, argc, argv) - generic pure */
  JS_CFUNC_PURE_0,  /* JSValue f(ctx) */
  JS_CFUNC_PURE_1,  /* JSValue f(ctx, arg0) */
  JS_CFUNC_PURE_2,  /* JSValue f(ctx, arg0, arg1) */
  JS_CFUNC_PURE_3,  /* JSValue f(ctx, arg0, arg1, arg2) */
  JS_CFUNC_PURE_4   /* JSValue f(ctx, arg0, arg1, arg2, arg3) */
} JSCFunctionEnum;

/* Fixed-arity C function types for fast paths */
typedef JSValue JSCFunction0 (JSContext *ctx, JSValue this_val);
typedef JSValue JSCFunction1 (JSContext *ctx, JSValue this_val,
                              JSValue arg0);
typedef JSValue JSCFunction2 (JSContext *ctx, JSValue this_val,
                              JSValue arg0, JSValue arg1);
typedef JSValue JSCFunction3 (JSContext *ctx, JSValue this_val,
                              JSValue arg0, JSValue arg1,
                              JSValue arg2);
typedef JSValue JSCFunction4 (JSContext *ctx, JSValue this_val,
                              JSValue arg0, JSValue arg1,
                              JSValue arg2, JSValue arg3);

/* Pure function types (no this_val) */
typedef JSValue JSCFunctionPure (JSContext *ctx, int argc, JSValue *argv);
typedef JSValue JSCFunctionPure0 (JSContext *ctx);
typedef JSValue JSCFunctionPure1 (JSContext *ctx, JSValue arg0);
typedef JSValue JSCFunctionPure2 (JSContext *ctx, JSValue arg0, JSValue arg1);
typedef JSValue JSCFunctionPure3 (JSContext *ctx, JSValue arg0, JSValue arg1,
                                   JSValue arg2);
typedef JSValue JSCFunctionPure4 (JSContext *ctx, JSValue arg0, JSValue arg1,
                                   JSValue arg2, JSValue arg3);

typedef union JSCFunctionType {
  JSCFunction *generic;
  JSValue (*generic_magic) (JSContext *ctx, JSValue this_val, int argc,
                            JSValue *argv, int magic);
  double (*f_f) (double);
  double (*f_f_f) (double, double);
  /* Fixed-arity fast paths */
  JSCFunction0 *f0;
  JSCFunction1 *f1;
  JSCFunction2 *f2;
  JSCFunction3 *f3;
  JSCFunction4 *f4;
  /* Pure function pointers */
  JSCFunctionPure *pure;
  JSCFunctionPure0 *pure0;
  JSCFunctionPure1 *pure1;
  JSCFunctionPure2 *pure2;
  JSCFunctionPure3 *pure3;
  JSCFunctionPure4 *pure4;
} JSCFunctionType;

JSValue JS_NewCFunction2 (JSContext *ctx, JSCFunction *func, const char *name,
                          int length, JSCFunctionEnum cproto, int magic);

static inline JSValue JS_NewCFunction (JSContext *ctx, JSCFunction *func, const char *name, int length) {
  return JS_NewCFunction2 (ctx, func, name, length, JS_CFUNC_generic, 0);
}

static inline JSValue JS_NewCFunctionMagic (JSContext *ctx, JSCFunctionMagic *func, const char *name, int length, JSCFunctionEnum cproto, int magic) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, length, cproto,
                           magic);
}

/* Fixed-arity fast path constructors */
static inline JSValue JS_NewCFuncFixed0 (JSContext *ctx, JSCFunction0 *func, const char *name) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 0, JS_CFUNC_0, 0);
}

static inline JSValue JS_NewCFuncFixed1 (JSContext *ctx, JSCFunction1 *func, const char *name) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 1, JS_CFUNC_1, 0);
}

static inline JSValue JS_NewCFuncFixed2 (JSContext *ctx, JSCFunction2 *func, const char *name) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 2, JS_CFUNC_2, 0);
}

static inline JSValue JS_NewCFuncFixed3 (JSContext *ctx, JSCFunction3 *func, const char *name) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 3, JS_CFUNC_3, 0);
}

static inline JSValue JS_NewCFuncFixed4 (JSContext *ctx, JSCFunction4 *func, const char *name) {
  return JS_NewCFunction2 (ctx, (JSCFunction *)func, name, 4, JS_CFUNC_4, 0);
}

/* C property definition */

typedef struct JSCFunctionListEntry {
  const char *name;
  uint8_t prop_flags;
  uint8_t def_type;
  int16_t magic;
  union {
    struct {
      uint8_t length; /* XXX: should move outside union */
      uint8_t cproto; /* XXX: should move outside union */
      JSCFunctionType cfunc;
    } func;
    struct {
      const char *name;
      int base;
    } alias;
    struct {
      const struct JSCFunctionListEntry *tab;
      int len;
    } prop_list;
    const char *str;
    int32_t i32;
    int64_t i64;
    double f64;
  } u;
} JSCFunctionListEntry;

#define JS_DEF_CFUNC 0
#define JS_DEF_PROP_STRING 3
#define JS_DEF_PROP_INT32 4
#define JS_DEF_PROP_INT64 5
#define JS_DEF_PROP_DOUBLE 6
#define JS_DEF_PROP_UNDEFINED 7
#define JS_DEF_OBJECT 8
#define JS_DEF_ALIAS 9

/* Note: c++ does not like nested designators */
#define JS_CFUNC_DEF(name, length, func1)                                     \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { length, JS_CFUNC_generic, { .generic = func1 } } }         \
  }
#define JS_CFUNC_MAGIC_DEF(name, length, func1, magic)                        \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, magic, .u = {                                      \
      .func = { length, JS_CFUNC_generic_magic, { .generic_magic = func1 } }  \
    }                                                                         \
  }
#define JS_CFUNC_SPECIAL_DEF(name, length, cproto, func1)                     \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { length, JS_CFUNC_##cproto, { .cproto = func1 } } }         \
  }
/* Fixed-arity fast path macros */
#define JS_CFUNC0_DEF(name, func1)                                            \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 0, JS_CFUNC_0, { .f0 = func1 } } }                         \
  }
#define JS_CFUNC1_DEF(name, func1)                                            \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 1, JS_CFUNC_1, { .f1 = func1 } } }                         \
  }
#define JS_CFUNC2_DEF(name, func1)                                            \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 2, JS_CFUNC_2, { .f2 = func1 } } }                         \
  }
#define JS_CFUNC3_DEF(name, func1)                                            \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 3, JS_CFUNC_3, { .f3 = func1 } } }                         \
  }
/* Pure function (no this_val) macros */
#define JS_CFUNC_PURE_DEF(name, length, func1)                                \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { length, JS_CFUNC_PURE, { .pure = func1 } } }               \
  }
#define JS_CFUNC_PURE0_DEF(name, func1)                                       \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 0, JS_CFUNC_PURE_0, { .pure0 = func1 } } }                 \
  }
#define JS_CFUNC_PURE1_DEF(name, func1)                                       \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 1, JS_CFUNC_PURE_1, { .pure1 = func1 } } }                 \
  }
#define JS_CFUNC_PURE2_DEF(name, func1)                                       \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 2, JS_CFUNC_PURE_2, { .pure2 = func1 } } }                 \
  }
#define JS_CFUNC_PURE3_DEF(name, func1)                                       \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 3, JS_CFUNC_PURE_3, { .pure3 = func1 } } }                 \
  }
#define JS_CFUNC_PURE4_DEF(name, func1)                                       \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, 0,                                                 \
      .u                                                                      \
      = {.func = { 4, JS_CFUNC_PURE_4, { .pure4 = func1 } } }                 \
  }
#define JS_ITERATOR_NEXT_DEF(name, length, func1, magic)                      \
  {                                                                           \
    name, 0, JS_DEF_CFUNC, magic, .u = {                                      \
      .func = { length, JS_CFUNC_iterator_next, { .iterator_next = func1 } }  \
    }                                                                         \
  }
#define JS_PROP_STRING_DEF(name, cstr, prop_flags)                            \
  {                                                                           \
    name, prop_flags, JS_DEF_PROP_STRING, 0, .u = {.str = cstr }              \
  }
#define JS_PROP_INT32_DEF(name, val, prop_flags)                              \
  {                                                                           \
    name, prop_flags, JS_DEF_PROP_INT32, 0, .u = {.i32 = val }                \
  }
#define JS_PROP_INT64_DEF(name, val, prop_flags)                              \
  {                                                                           \
    name, prop_flags, JS_DEF_PROP_INT64, 0, .u = {.i64 = val }                \
  }
#define JS_PROP_DOUBLE_DEF(name, val, prop_flags)                             \
  {                                                                           \
    name, prop_flags, JS_DEF_PROP_DOUBLE, 0, .u = {.f64 = val }               \
  }
#define JS_PROP_UNDEFINED_DEF(name, prop_flags)                               \
  { name, prop_flags, JS_DEF_PROP_UNDEFINED, 0, .u = { .i32 = 0 } }
#define JS_OBJECT_DEF(name, tab, len, prop_flags)                             \
  {                                                                           \
    name, prop_flags, JS_DEF_OBJECT, 0, .u = {.prop_list = { tab, len } }     \
  }
#define JS_ALIAS_DEF(name, from)                                              \
  {                                                                           \
    name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0,           \
      .u                                                                      \
      = {.alias = { from, -1 } }                                              \
  }
#define JS_ALIAS_BASE_DEF(name, from, base)                                   \
  {                                                                           \
    name, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_ALIAS, 0,           \
      .u                                                                      \
      = {.alias = { from, base } }                                            \
  }

int JS_SetPropertyFunctionList (JSContext *ctx, JSValue obj,
                                const JSCFunctionListEntry *tab, int len);

/* ============================================================
   11. Debug / Dump Utilities
   ============================================================ */

typedef struct JSMemoryUsage {
  int64_t malloc_size, malloc_limit, memory_used_size;
  int64_t malloc_count;
  int64_t memory_used_count;
  int64_t str_count, str_size;
  int64_t obj_count, obj_size;
  int64_t prop_count, prop_size;
  int64_t shape_count, shape_size;
  int64_t js_func_count, js_func_size, js_func_code_size;
  int64_t js_func_pc2line_count, js_func_pc2line_size;
  int64_t c_func_count, array_count;
  int64_t fast_array_count, fast_array_elements;
  int64_t binary_object_count, binary_object_size;
} JSMemoryUsage;

void JS_ComputeMemoryUsage (JSRuntime *rt, JSMemoryUsage *s);
void JS_DumpMemoryUsage (FILE *fp, const JSMemoryUsage *s, JSRuntime *rt);

typedef struct {
  JS_BOOL show_hidden : 8; /* only show enumerable properties */
  JS_BOOL raw_dump : 8;    /* avoid doing autoinit and avoid any malloc() call
                              (for internal use) */
  uint32_t max_depth;      /* recurse up to this depth, 0 = no limit */
  uint32_t max_string_length; /* print no more than this length for
                                 strings, 0 = no limit */
  uint32_t max_item_count;    /*  print no more than this count for
                                  arrays or objects, 0 = no limit */
} JSPrintValueOptions;

typedef void JSPrintValueWrite (void *opaque, const char *buf, size_t len);

void JS_PrintValueSetDefaultOptions (JSPrintValueOptions *options);
void JS_PrintValueRT (JSRuntime *rt, JSPrintValueWrite *write_func,
                      void *write_opaque, JSValue val,
                      const JSPrintValueOptions *options);
void JS_PrintValue (JSContext *ctx, JSPrintValueWrite *write_func,
                    void *write_opaque, JSValue val,
                    const JSPrintValueOptions *options);

typedef struct js_debug {
  char name[64];
  char filename[96];
  int unique;
  int line;
  int param_n;
  int closure_n;
  int vararg;
  const char *what;
  const uint8_t *source;
  int srclen;
} js_debug;

void js_debug_info (JSContext *js, JSValue fn, js_debug *dbg);

typedef void (*js_hook) (JSContext *, int type, js_debug *dbg, void *user);
#define JS_HOOK_CALL 1
#define JS_HOOK_RET 2
#define JS_HOOK_CYCLE 4
#define JS_HOOK_GC 8
void js_debug_sethook (JSContext *ctx, js_hook, int type, void *user);

uint32_t js_debugger_stack_depth (JSContext *ctx);
JSValue js_debugger_backtrace_fns (JSContext *ctx);
JSValue js_debugger_closure_variables (JSContext *ctx, JSValue fn);
JSValue js_debugger_local_variables (JSContext *ctx, int stack_index);
void js_debugger_set_closure_variable (JSContext *js, JSValue fn,
                                       JSValue var_name, JSValue val);
JSValue js_debugger_build_backtrace (JSContext *ctx);
JSValue js_debugger_fn_info (JSContext *ctx, JSValue fn);
JSValue js_debugger_fn_bytecode (JSContext *js, JSValue fn);
void *js_debugger_val_address (JSContext *js, JSValue val);

/* ============================================================
   12. Memory Allocation
   ============================================================ */
void *js_malloc (JSContext *ctx, size_t size);
void *js_mallocz (JSContext *ctx, size_t size);
void *js_realloc (JSContext *ctx, void *ptr, size_t size);
void js_free (JSContext *ctx, void *ptr);
char *js_strdup (JSContext *ctx, const char *str);

/* Runtime-level memory functions */
void *js_malloc_rt (size_t size);
void *js_mallocz_rt (size_t size);
void js_free_rt (void *ptr);

/* ============================================================
   13. Compilation and Bytecode
   ============================================================ */

struct cJSON;
typedef struct MachCode MachCode;

/* Free a compiled MachCode tree. */
void JS_FreeMachCode(MachCode *mc);

/* Serialize MachCode to binary. Returns sys_malloc'd buffer; caller frees. */
uint8_t *JS_SerializeMachCode(MachCode *mc, size_t *out_size);

/* Deserialize binary back to MachCode. Returns NULL on error. */
MachCode *JS_DeserializeMachCode(const uint8_t *data, size_t size);

/* Load compiled MachCode into a JSContext, materializing JSValues. */
struct JSCodeRegister *JS_LoadMachCode(JSContext *ctx, MachCode *mc, JSValue env);

/* Deserialize and execute pre-compiled MACH binary bytecode. */
JSValue JS_RunMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);

/* Parse mcode JSON IR, compile, and execute via register VM. */
JSValue JS_RunMachMcode(JSContext *ctx, const char *json_str, size_t len, JSValue env);

/* Dump disassembly of pre-compiled MACH binary bytecode. */
void JS_DumpMachBin(JSContext *ctx, const uint8_t *data, size_t size, JSValue env);

/* Compile mcode JSON IR to MachCode binary. */
MachCode *mach_compile_mcode(struct cJSON *mcode_json);

/* Get stack trace as JS array of {fn, file, line, col} objects.
   Returns an empty array if no register VM frame is active.
   Does NOT clear reg_current_frame — caller is responsible if needed. */
JSValue JS_GetStack (JSContext *ctx);

#undef js_unlikely
#undef inline

#ifdef __cplusplus
} /* extern "C" { */
#endif

#endif /* QUICKJS_H */
