// sys_playdate.c - Cell integration for Playdate System API
// Wraps pd_api_sys.h functions for JavaScript access

#include "cell.h"
#include "common.h"
#include <string.h>

// --- System Functions ---

JSC_CCALL(sys_logToConsole,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    const char *str = JS_ToCString(js, argv[0]);
    if (str) {
        pd_sys->logToConsole("%s", str);
        JS_FreeCString(js, str);
    }
    return JS_NULL;
)

JSC_CCALL(sys_error,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    const char *str = JS_ToCString(js, argv[0]);
    if (str) {
        pd_sys->error("%s", str);
        JS_FreeCString(js, str);
    }
    return JS_NULL;
)

JSC_CCALL(sys_getLanguage,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    PDLanguage lang = pd_sys->getLanguage();
    return JS_NewInt32(js, lang);
)

JSC_CCALL(sys_getCurrentTimeMilliseconds,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewInt64(js, pd_sys->getCurrentTimeMilliseconds());
)

JSC_CCALL(sys_getSecondsSinceEpoch,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    unsigned int ms = 0;
    unsigned int secs = pd_sys->getSecondsSinceEpoch(&ms);
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "seconds", JS_NewInt64(js, secs));
    JS_SetPropertyStr(js, obj, "milliseconds", JS_NewInt32(js, ms));
    return obj;
)

JSC_CCALL(sys_drawFPS,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    int x = (int)js2number(js, argv[0]);
    int y = (int)js2number(js, argv[1]);
    pd_sys->drawFPS(x, y);
    return JS_NULL;
)

JSC_CCALL(sys_getButtonState,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    PDButtons current, pushed, released;
    pd_sys->getButtonState(&current, &pushed, &released);
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "current", JS_NewInt32(js, current));
    JS_SetPropertyStr(js, obj, "pushed", JS_NewInt32(js, pushed));
    JS_SetPropertyStr(js, obj, "released", JS_NewInt32(js, released));
    return obj;
)

JSC_CCALL(sys_setPeripheralsEnabled,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    PDPeripherals mask = (PDPeripherals)(int)js2number(js, argv[0]);
    pd_sys->setPeripheralsEnabled(mask);
    return JS_NULL;
)

JSC_CCALL(sys_getAccelerometer,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    float x, y, z;
    pd_sys->getAccelerometer(&x, &y, &z);
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "x", JS_NewFloat64(js, x));
    JS_SetPropertyStr(js, obj, "y", JS_NewFloat64(js, y));
    JS_SetPropertyStr(js, obj, "z", JS_NewFloat64(js, z));
    return obj;
)

JSC_CCALL(sys_getCrankChange,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewFloat64(js, pd_sys->getCrankChange());
)

JSC_CCALL(sys_getCrankAngle,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewFloat64(js, pd_sys->getCrankAngle());
)

JSC_CCALL(sys_isCrankDocked,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewBool(js, pd_sys->isCrankDocked());
)

JSC_CCALL(sys_setCrankSoundsDisabled,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    int flag = JS_ToBool(js, argv[0]);
    int prev = pd_sys->setCrankSoundsDisabled(flag);
    return JS_NewBool(js, prev);
)

JSC_CCALL(sys_getFlipped,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewBool(js, pd_sys->getFlipped());
)

JSC_CCALL(sys_setAutoLockDisabled,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->setAutoLockDisabled(JS_ToBool(js, argv[0]));
    return JS_NULL;
)

JSC_CCALL(sys_getReduceFlashing,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewBool(js, pd_sys->getReduceFlashing());
)

JSC_CCALL(sys_getElapsedTime,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewFloat64(js, pd_sys->getElapsedTime());
)

JSC_CCALL(sys_resetElapsedTime,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->resetElapsedTime();
    return JS_NULL;
)

JSC_CCALL(sys_getBatteryPercentage,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewFloat64(js, pd_sys->getBatteryPercentage());
)

JSC_CCALL(sys_getBatteryVoltage,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewFloat64(js, pd_sys->getBatteryVoltage());
)

JSC_CCALL(sys_getTimezoneOffset,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewInt32(js, pd_sys->getTimezoneOffset());
)

JSC_CCALL(sys_shouldDisplay24HourTime,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    return JS_NewBool(js, pd_sys->shouldDisplay24HourTime());
)

JSC_CCALL(sys_convertEpochToDateTime,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    uint32_t epoch = (uint32_t)js2number(js, argv[0]);
    struct PDDateTime dt;
    pd_sys->convertEpochToDateTime(epoch, &dt);
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "year", JS_NewInt32(js, dt.year));
    JS_SetPropertyStr(js, obj, "month", JS_NewInt32(js, dt.month));
    JS_SetPropertyStr(js, obj, "day", JS_NewInt32(js, dt.day));
    JS_SetPropertyStr(js, obj, "weekday", JS_NewInt32(js, dt.weekday));
    JS_SetPropertyStr(js, obj, "hour", JS_NewInt32(js, dt.hour));
    JS_SetPropertyStr(js, obj, "minute", JS_NewInt32(js, dt.minute));
    JS_SetPropertyStr(js, obj, "second", JS_NewInt32(js, dt.second));
    return obj;
)

JSC_CCALL(sys_convertDateTimeToEpoch,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    struct PDDateTime dt = {0};
    dt.year = (uint16_t)js2number(js, JS_GetPropertyStr(js, argv[0], "year"));
    dt.month = (uint8_t)js2number(js, JS_GetPropertyStr(js, argv[0], "month"));
    dt.day = (uint8_t)js2number(js, JS_GetPropertyStr(js, argv[0], "day"));
    dt.hour = (uint8_t)js2number(js, JS_GetPropertyStr(js, argv[0], "hour"));
    dt.minute = (uint8_t)js2number(js, JS_GetPropertyStr(js, argv[0], "minute"));
    dt.second = (uint8_t)js2number(js, JS_GetPropertyStr(js, argv[0], "second"));
    return JS_NewInt64(js, pd_sys->convertDateTimeToEpoch(&dt));
)

JSC_CCALL(sys_clearICache,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->clearICache();
    return JS_NULL;
)

JSC_CCALL(sys_delay,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->delay((uint32_t)js2number(js, argv[0]));
    return JS_NULL;
)

JSC_SCALL(sys_restartGame,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->restartGame(str);
)

JSC_CCALL(sys_getLaunchArgs,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    const char *path = NULL;
    const char *args = pd_sys->getLaunchArgs(&path);
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "args", args ? JS_NewString(js, args) : JS_NULL);
    JS_SetPropertyStr(js, obj, "path", path ? JS_NewString(js, path) : JS_NULL);
    return obj;
)

JSC_CCALL(sys_getSystemInfo,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    const struct PDInfo *info = pd_sys->getSystemInfo();
    if (!info) return JS_NULL;
    JSValue obj = JS_NewObject(js);
    JS_SetPropertyStr(js, obj, "osversion", JS_NewInt64(js, info->osversion));
    JS_SetPropertyStr(js, obj, "language", JS_NewInt32(js, info->language));
    return obj;
)

// --- Menu Functions ---

JSC_CCALL(sys_removeAllMenuItems,
    if (!pd_sys) return JS_RaiseDisrupt(js, "system not initialized");
    pd_sys->removeAllMenuItems();
    return JS_NULL;
)

static const JSCFunctionListEntry js_sys_funcs[] = {
    MIST_FUNC_DEF(sys, logToConsole, 1),
    MIST_FUNC_DEF(sys, error, 1),
    MIST_FUNC_DEF(sys, getLanguage, 0),
    MIST_FUNC_DEF(sys, getCurrentTimeMilliseconds, 0),
    MIST_FUNC_DEF(sys, getSecondsSinceEpoch, 0),
    MIST_FUNC_DEF(sys, drawFPS, 2),
    MIST_FUNC_DEF(sys, getButtonState, 0),
    MIST_FUNC_DEF(sys, setPeripheralsEnabled, 1),
    MIST_FUNC_DEF(sys, getAccelerometer, 0),
    MIST_FUNC_DEF(sys, getCrankChange, 0),
    MIST_FUNC_DEF(sys, getCrankAngle, 0),
    MIST_FUNC_DEF(sys, isCrankDocked, 0),
    MIST_FUNC_DEF(sys, setCrankSoundsDisabled, 1),
    MIST_FUNC_DEF(sys, getFlipped, 0),
    MIST_FUNC_DEF(sys, setAutoLockDisabled, 1),
    MIST_FUNC_DEF(sys, getReduceFlashing, 0),
    MIST_FUNC_DEF(sys, getElapsedTime, 0),
    MIST_FUNC_DEF(sys, resetElapsedTime, 0),
    MIST_FUNC_DEF(sys, getBatteryPercentage, 0),
    MIST_FUNC_DEF(sys, getBatteryVoltage, 0),
    MIST_FUNC_DEF(sys, getTimezoneOffset, 0),
    MIST_FUNC_DEF(sys, shouldDisplay24HourTime, 0),
    MIST_FUNC_DEF(sys, convertEpochToDateTime, 1),
    MIST_FUNC_DEF(sys, convertDateTimeToEpoch, 1),
    MIST_FUNC_DEF(sys, clearICache, 0),
    MIST_FUNC_DEF(sys, delay, 1),
    MIST_FUNC_DEF(sys, restartGame, 1),
    MIST_FUNC_DEF(sys, getLaunchArgs, 0),
    MIST_FUNC_DEF(sys, getSystemInfo, 0),
    MIST_FUNC_DEF(sys, removeAllMenuItems, 0),
};

JSValue js_sys_use(JSContext *js) {
    JSValue mod = JS_NewObject(js);
    JS_SetPropertyFunctionList(js, mod, js_sys_funcs, countof(js_sys_funcs));
    return mod;
}
