/*
 * stat — POSIX stat() поверх метаданных ESTEX.
 *
 *   stat(path, &st) -> open(O_RDONLY) + fstat() + close.  Работает для
 *                      любого обычного файла; для директорий — fallback
 *                      через F_FIRST (и перебор "*.*" для "."/"..").
 */

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>
#include <errno.h>
#include <stdint.h>
#include <string.h>
#include <dir.h>

/* Конвертация DOS-формата даты+времени ESTEX в time_t (используется
 * на пути через F_FIRST для записей директорий). */
static time_t dos_to_epoch(uint16_t date, uint16_t dtime)
{
    struct tm tm;
    tm.tm_sec   = (unsigned char)((dtime & 0x1F) << 1);
    tm.tm_min   = (unsigned char)((dtime >> 5) & 0x3F);
    tm.tm_hour  = (unsigned char)((dtime >> 11) & 0x1F);
    tm.tm_mday  = (unsigned char)(date & 0x1F);
    tm.tm_mon   = (unsigned char)(((date >> 5) & 0x0F) - 1);
    tm.tm_year  = (int)((date >> 9) & 0x7F) + 80;   /* DOS year base = 1980 */
    tm.tm_isdst = 0;
    tm.tm_hundredth = 0;
    return mktime(&tm);
}

/* Возвращает 1, если path — "." или "..", иначе 0.  Читает максимум
 * 3 байта; NULL-безопасно.  ~28 байт / 34–117 тактов от входа. */
static char is_dot_or_dotdot(const char *path) __naked
{
    (void)path;
    __asm
        ld      a, h
        or      a, l
        jr      Z, _idd_fail            ; NULL → 0
        ld      a, (hl)
        sub     a, #0x2E
        jr      NZ, _idd_fail           ; path[0] != '.'
        inc     hl
        ld      a, (hl)
        or      a, a
        jr      Z, _idd_ok              ; ".\0" → 1
        sub     a, #0x2E
        jr      NZ, _idd_fail           ; path[1] not '.' and not '\0'
        inc     hl
        ld      a, (hl)
        or      a, a
        jr      Z, _idd_ok              ; "..\0" → 1
    _idd_fail:
        xor     a, a
        ret
    _idd_ok:
        ld      a, #1
        ret
    __endasm;
}

int stat(const char *path, struct stat *buf)
{
    /* Обычный файл: open + fstat. */
    int fd = open(path, O_RDONLY);
    if (fd >= 0) {
        int r = fstat(fd, buf);
        close(fd);
        return r;
    }
    int saved = errno;

    /* Пробуем ffirst напрямую — работает для обычных подпапок. */
    ffblk_t ffb;
    if (ffirst(path, &ffb, FA_DIREC) == 0 && (ffb.found_attr & FA_DIREC)) {
        buf->st_size  = ffb.size;
        buf->st_mtime = dos_to_epoch(ffb.date, ffb.time);
        buf->st_mode  = S_IFDIR | S_IRWXU;
        return 0;
    }

    /* Проверено 2026-05-29: ESTEX F_FIRST отвергает голые "." и ".."
     * с EINAME (16), как и open().  Но в листинге "*.*" они ЕСТЬ с
     * FA_DIREC — ищем перебором. */
    if (is_dot_or_dotdot(path)) {
        if (ffirst("*.*", &ffb, FA_DIREC) == 0) {
            do {
                if (strcmp(ffb.found_name, path) == 0) {
                    buf->st_size  = ffb.size;
                    buf->st_mtime = dos_to_epoch(ffb.date, ffb.time);
                    buf->st_mode  = S_IFDIR | S_IRWXU;
                    return 0;
                }
            } while (fnext(&ffb) == 0);
        }
        /* Крайний случай — синтетическая запись: ФС их не отдала. */
        buf->st_mode  = S_IFDIR | S_IRWXU;
        buf->st_size  = 0;
        buf->st_mtime = 0;
        return 0;
    }

    errno = saved;
    return -1;
}
