// file_playdate.c - Cell integration for Playdate File API
// Wraps pd_api_file.h functions for JavaScript access
// Note: fd_playdate.c already provides the main file operations.
// This module provides additional raw file API access.

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

// --- Helpers ---
static SDFile* js2sdfile(JSContext *js, JSValueConst val) {
    int64_t ptr;
    if (JS_ToInt64(js, &ptr, val) < 0) return NULL;
    return (SDFile*)(intptr_t)ptr;
}

// --- File Functions ---

JSC_CCALL(file_geterr,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    const char *err = pd_file->geterr();
    return err ? JS_NewString(js, err) : JS_NULL;
)

JSC_SCALL(file_stat,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    FileStat st;
    if (pd_file->stat(str, &st) != 0) {
        ret = JS_NULL;
    } else {
        JSValue obj = JS_NewObject(js);
        JS_SetPropertyStr(js, obj, "isdir", JS_NewBool(js, st.isdir));
        JS_SetPropertyStr(js, obj, "size", JS_NewInt64(js, st.size));
        JS_SetPropertyStr(js, obj, "year", JS_NewInt32(js, st.m_year));
        JS_SetPropertyStr(js, obj, "month", JS_NewInt32(js, st.m_month));
        JS_SetPropertyStr(js, obj, "day", JS_NewInt32(js, st.m_day));
        JS_SetPropertyStr(js, obj, "hour", JS_NewInt32(js, st.m_hour));
        JS_SetPropertyStr(js, obj, "minute", JS_NewInt32(js, st.m_minute));
        JS_SetPropertyStr(js, obj, "second", JS_NewInt32(js, st.m_second));
        ret = obj;
    }
)

JSC_SCALL(file_mkdir,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    ret = JS_NewBool(js, pd_file->mkdir(str) == 0);
)

JSC_SCALL(file_unlink,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    int recursive = argc > 1 ? JS_ToBool(js, argv[1]) : 0;
    ret = JS_NewBool(js, pd_file->unlink(str, recursive) == 0);
)

JSC_SCALL(file_rename,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    const char *to = JS_ToCString(js, argv[1]);
    int result = pd_file->rename(str, to);
    JS_FreeCString(js, to);
    ret = JS_NewBool(js, result == 0);
)

JSC_SCALL(file_open,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    FileOptions mode = kFileRead;
    if (argc > 1) mode = (FileOptions)(int)js2number(js, argv[1]);
    SDFile *f = pd_file->open(str, mode);
    ret = f ? JS_NewInt64(js, (int64_t)(intptr_t)f) : JS_NULL;
)

JSC_CCALL(file_close,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    return JS_NewBool(js, pd_file->close(f) == 0);
)

JSC_CCALL(file_read,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    unsigned int len = (unsigned int)js2number(js, argv[1]);
    void *out;
    JSValue blob = js_new_blob_alloc(js, len, &out);
    if (JS_IsException(blob)) return blob;
    int bytes_read = pd_file->read(f, out, len);
    if (bytes_read < 0)
        return JS_NULL;
    js_blob_stone(blob, bytes_read);
    return blob;
)

JSC_CCALL(file_write,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    size_t len;
    const void *data = js_get_blob_data(js, &len, argv[1]);
    if (data == (void*)-1) return JS_EXCEPTION;
    int written = pd_file->write(f, data, (unsigned int)len);
    return JS_NewInt32(js, written);
)

JSC_CCALL(file_flush,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    return JS_NewBool(js, pd_file->flush(f) == 0);
)

JSC_CCALL(file_tell,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    return JS_NewInt32(js, pd_file->tell(f));
)

JSC_CCALL(file_seek,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    SDFile *f = js2sdfile(js, argv[0]);
    int pos = (int)js2number(js, argv[1]);
    int whence = argc > 2 ? (int)js2number(js, argv[2]) : SEEK_SET;
    return JS_NewBool(js, pd_file->seek(f, pos, whence) == 0);
)

struct listfiles_ctx {
    JSContext *js;
    JSValue array;
    int index;
};

static void listfiles_cb(const char *path, void *userdata) {
    struct listfiles_ctx *ctx = (struct listfiles_ctx*)userdata;
    JS_SetPropertyNumber(ctx->js, ctx->array, ctx->index++, JS_NewString(ctx->js, path));
}

JSC_SCALL(file_listfiles,
    if (!pd_file) return JS_RaiseDisrupt(js, "file not initialized");
    int showhidden = argc > 1 ? JS_ToBool(js, argv[1]) : 0;
    JSValue arr = JS_NewArray(js);
    struct listfiles_ctx ctx = { js, arr, 0 };
    if (pd_file->listfiles(str, listfiles_cb, &ctx, showhidden) != 0) {
        JS_FreeValue(js, arr);
        ret = JS_NULL;
    } else {
        ret = arr;
    }
)

static const JSCFunctionListEntry js_file_funcs[] = {
    MIST_FUNC_DEF(file, geterr, 0),
    MIST_FUNC_DEF(file, stat, 1),
    MIST_FUNC_DEF(file, mkdir, 1),
    MIST_FUNC_DEF(file, unlink, 2),
    MIST_FUNC_DEF(file, rename, 2),
    MIST_FUNC_DEF(file, open, 2),
    MIST_FUNC_DEF(file, close, 1),
    MIST_FUNC_DEF(file, read, 2),
    MIST_FUNC_DEF(file, write, 2),
    MIST_FUNC_DEF(file, flush, 1),
    MIST_FUNC_DEF(file, tell, 1),
    MIST_FUNC_DEF(file, seek, 3),
    MIST_FUNC_DEF(file, listfiles, 2),
};

JSValue js_file_use(JSContext *js) {
    JSValue mod = JS_NewObject(js);
    JS_SetPropertyFunctionList(js, mod, js_file_funcs, countof(js_file_funcs));
    // Export constants
    JS_SetPropertyStr(js, mod, "READ", JS_NewInt32(js, kFileRead));
    JS_SetPropertyStr(js, mod, "READ_DATA", JS_NewInt32(js, kFileReadData));
    JS_SetPropertyStr(js, mod, "WRITE", JS_NewInt32(js, kFileWrite));
    JS_SetPropertyStr(js, mod, "APPEND", JS_NewInt32(js, kFileAppend));
    JS_SetPropertyStr(js, mod, "SEEK_SET", JS_NewInt32(js, SEEK_SET));
    JS_SetPropertyStr(js, mod, "SEEK_CUR", JS_NewInt32(js, SEEK_CUR));
    JS_SetPropertyStr(js, mod, "SEEK_END", JS_NewInt32(js, SEEK_END));
    return mod;
}
