// os_playdate.c - OS module for Playdate
// Provides system information and utilities for Playdate platform

#include "cell.h"
#include "pd_api.h"
#include <stdlib.h>
#include <string.h>

// Global Playdate API pointers - defined in main_playdate.c
extern const struct playdate_sys *pd_sys;

// cell_ns - nanoseconds since some epoch (used by scheduler)
uint64_t cell_ns(void)
{
    // Playdate provides milliseconds, convert to nanoseconds
    if (pd_sys) {
        unsigned int ms = pd_sys->getCurrentTimeMilliseconds();
        return (uint64_t)ms * 1000000ULL;
    }
    return 0;
}

// cell_sleep - sleep for specified seconds
void cell_sleep(double seconds)
{
    if (pd_sys) {
        pd_sys->delay((uint32_t)(seconds * 1000));
    }
}

JSC_CCALL(os_now, 
    return number2js(js, cell_ns()); 
)

JSC_CCALL(os_sleep,
    double secs = js2number(js, argv[0]);
    cell_sleep(secs);
)

static JSValue js_os_totalmem(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // Playdate has 16MB RAM, but we don't have direct access to memory stats
    return JS_NewInt64(js, 16); // 16 MB
}

static JSValue js_os_platform(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "Playdate");
}

static JSValue js_os_hostname(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "playdate");
}

static JSValue js_os_arch(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewString(js, "arm");
}

static JSValue js_os_freemem(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // No way to get free memory on Playdate
    return JS_NewInt64(js, 0);
}

static JSValue js_os_version(JSContext *js, JSValue self, int argc, JSValue *argv) {
#if TARGET_EXTENSION
    if (pd_sys) {
        const struct PDInfo *info = pd_sys->getSystemInfo();
        if (info) {
            char buf[32];
            snprintf(buf, sizeof(buf), "%u", info->osversion);
            return JS_NewString(js, buf);
        }
    }
#endif
    return JS_NewString(js, "unknown");
}

JSC_CCALL(os_mallinfo,
    ret = JS_NewObject(js);
)

static JSValue js_os_rusage(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // No rusage on Playdate
    return JS_NewObject(js);
}

JSC_SCALL(os_system,
    // system() not available on Playdate
    ret = JS_NewInt32(js, -1);
)

JSC_CCALL(os_exit,
    // Can't really exit on Playdate, but we can try
    exit(0);
)

// dylib functions - not supported on Playdate
static JSValue js_os_dylib_open(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_ThrowInternalError(js, "dylib_open: not supported on Playdate");
}

static JSValue js_os_dylib_symbol(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_ThrowInternalError(js, "dylib_symbol: not supported on Playdate");
}

static JSValue js_os_dylib_has_symbol(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewBool(js, 0);
}

JSC_CCALL(os_print,
    size_t len;
    const char *str = JS_ToCStringLen(js, &len, argv[0]);
    if (pd_sys) {
        pd_sys->logToConsole("%.*s", (int)len, str);
    }
    JS_FreeCString(js, str);
)

static JSValue js_os_load_internal(JSContext *js, JSValue self, int argc, JSValue *argv) {
    // No dlsym on Playdate - internal modules are linked statically
    return JS_NULL;
}

static JSValue js_os_internal_exists(JSContext *js, JSValue self, int argc, JSValue *argv) {
    return JS_NewBool(js, 0);
}

// Random number generation
int randombytes(void *buf, size_t n) {
    // Playdate doesn't have a crypto RNG, use a simple PRNG seeded from time
    // This is NOT cryptographically secure!
    uint8_t *p = (uint8_t *)buf;
    static uint32_t seed = 0;
    if (seed == 0 && pd_sys) {
        seed = pd_sys->getCurrentTimeMilliseconds();
    }
    for (size_t i = 0; i < n; i++) {
        seed = seed * 1103515245 + 12345;
        p[i] = (uint8_t)(seed >> 16);
    }
    return 0;
}

JSC_CCALL(os_random,
    return JS_NewFloat64(js, cell_random_fit());
)

JSC_CCALL(os_getenv,
    // No environment variables on Playdate
    return JS_NULL;
)

static const JSCFunctionListEntry js_os_funcs[] = {
    MIST_FUNC_DEF(os, platform, 0),
    MIST_FUNC_DEF(os, arch, 0),
    MIST_FUNC_DEF(os, totalmem, 0),
    MIST_FUNC_DEF(os, freemem, 0),
    MIST_FUNC_DEF(os, hostname, 0),
    MIST_FUNC_DEF(os, version, 0),
    MIST_FUNC_DEF(os, now, 0),
    MIST_FUNC_DEF(os, rusage, 0),
    MIST_FUNC_DEF(os, mallinfo, 0),
    MIST_FUNC_DEF(os, system, 1),
    MIST_FUNC_DEF(os, exit, 0),
    MIST_FUNC_DEF(os, sleep, 1),
    MIST_FUNC_DEF(os, dylib_open, 1),
    MIST_FUNC_DEF(os, dylib_symbol, 2),
    MIST_FUNC_DEF(os, dylib_has_symbol, 2),
    MIST_FUNC_DEF(os, load_internal, 1),
    MIST_FUNC_DEF(os, internal_exists, 1),
    MIST_FUNC_DEF(os, print, 1),
    MIST_FUNC_DEF(os, random, 0),
    MIST_FUNC_DEF(os, getenv, 1),
};

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