// Writable save files (SAVES/) and a disk-space query.
//
// Mirrors jlDataOpen's forced-prefix approach (see assetLoad.c) but rooted at
// the writable SAVES/ tree instead of read-only DATA/, and it creates SAVES/
// on demand before a write open. Blob read/write/exists are plain stdio over
// jlSaveOpen; only the two operations stdio cannot do portably -- create the
// directory and delete a file -- plus the disk-free query are per-port HAL
// hooks (jlpSaveDirEnsure / jlpSaveDelete / jlpDiskFree).

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

#include "joey/file.h"
#include "port.h"


// A port that must root the save tree elsewhere overrides these in port.h
// (IIgs: GS/OS prefix-designator "1/" = the application directory - unrooted
// paths resolve against prefix 0, which a Finder launch does not point at the
// app's volume, so plain "SAVES/x" opens fail there).
#ifndef JL_SAVE_DIR
#define JL_SAVE_DIR        "SAVES"
#define JL_SAVE_DIR_PREFIX "SAVES/"
#endif
#define SAVE_DIR          JL_SAVE_DIR
#define SAVE_DIR_PREFIX   JL_SAVE_DIR_PREFIX
#define SAVE_PATH_MAX     256


// ----- Helpers -----

static bool buildSavePath(char *out, size_t cap, const char *name);


// Join SAVE_DIR_PREFIX + name into `out`. Returns false if it would overflow.
static bool buildSavePath(char *out, size_t cap, const char *name) {
    int  written;

    written = snprintf(out, cap, SAVE_DIR_PREFIX "%s", name);
    return written >= 0 && (size_t)written < cap;
}


// ----- Public API (alphabetical) -----

uint32_t jlDiskFree(void) {
    return jlpDiskFree();
}


bool jlSaveDelete(const char *name) {
    char  path[SAVE_PATH_MAX];

    if (name == NULL) {
        return false;
    }
    if (!buildSavePath(path, sizeof(path), name)) {
        return false;
    }
    return jlpSaveDelete(path);
}


bool jlSaveExists(const char *name) {
    FILE  *fp;

    if (name == NULL) {
        return false;
    }
    fp = jlSaveOpen(name, "rb");
    if (fp == NULL) {
        return false;
    }
    fclose(fp);
    return true;
}


FILE *jlSaveOpen(const char *name, const char *mode) {
    char  path[SAVE_PATH_MAX];

    if (name == NULL || mode == NULL) {
        return NULL;
    }
    // Create SAVES/ before a write / append open. The result is advisory: if
    // the directory cannot be made, fopen below simply fails and the caller
    // sees NULL.
    if (strchr(mode, 'w') != NULL || strchr(mode, 'a') != NULL) {
        (void)jlpSaveDirEnsure(SAVE_DIR);
    }
    if (!buildSavePath(path, sizeof(path), name)) {
        return NULL;
    }
    return fopen(path, mode);
}


uint32_t jlSaveRead(const char *name, void *buf, uint32_t max) {
    FILE    *fp;
    size_t   got;

    if (name == NULL || buf == NULL || max == 0u) {
        return 0u;
    }
    fp = jlSaveOpen(name, "rb");
    if (fp == NULL) {
        return 0u;
    }
    got = fread(buf, 1u, (size_t)max, fp);
    fclose(fp);
    return (uint32_t)got;
}


bool jlSaveWrite(const char *name, const void *buf, uint32_t len) {
    FILE    *fp;
    size_t   written;

    if (name == NULL) {
        return false;
    }
    fp = jlSaveOpen(name, "wb");
    if (fp == NULL) {
        return false;
    }
    written = 0u;
    if (buf != NULL && len > 0u) {
        written = fwrite(buf, 1u, (size_t)len, fp);
    }
    fclose(fp);
    return written == (size_t)len;
}
