/*
 * fputc — записать один байт в поток (буферизованно, вариант B+).
 *
 * Горячий путь («поток уже копит запись и в буфере есть место»)
 * написан на asm — см. пояснение в fgetc.c.  Остальное — медленный
 * C-путь fputc_slow: консольный putchar(), переключение направления
 * (_file_sync откатывает readahead), сброс полного буфера, ленивое
 * выделение, fallback без буфера (прямой write() 1 байта).
 * ВНИМАНИЕ: успех fputc = «байт принят в буфер»; ошибка реальной
 * записи может вылезти позже (fflush/fclose).
 *
 * Раскладка FILE: +0 fd, +2 flags, +3 buf, +5 curp, +7 level, +9 hold.
 * asm рассчитывает на BUFSIZ == 512 (проверка уровня по старшему
 * байту level < 2).
 */

#include <unistd.h>
#include <errno.h>
#include "_file.h"

#if BUFSIZ != 512
#error "asm-горячий путь fputc рассчитан на BUFSIZ == 512"
#endif

/* Медленный путь; зовётся из asm-обёртки. */
static int fputc_slow(int c, FILE *fp)
{
#ifndef LIBC_NOCHECK
    if (!fp) { errno = EBADF; return EOF; }
#endif
    if (fp->flags & _F_CONOUT) {
        putchar(c);
        return (int)(uint8_t)c;
    }
    if (!(fp->flags & _F_WRITE)) { errno = EBADF; return EOF; }

    if (!(fp->flags & _F_DIROUT)) {
        if (_file_sync(fp)) return EOF;      /* откат readahead/hold */
    } else if (fp->level >= BUFSIZ) {
        if (_file_sync(fp)) return EOF;      /* буфер полон — сброс */
    }

    uint8_t *b = _file_buf(fp);
    if (!b) {
        /* Небуферизованный fallback. */
        uint8_t ch = (uint8_t)c;
        if (write(fp->fd, &ch, 1) != 1) {
            fp->flags |= _F_ERROR;
            return EOF;
        }
        return (int)ch;
    }
    if (!(fp->flags & _F_DIROUT)) {
        fp->flags |= _F_DIROUT;
        fp->curp   = b;
    }
    *fp->curp++ = (uint8_t)c;
    fp->level++;
    return (int)(uint8_t)c;
}

int fputc(int c, FILE *fp) __naked
{
    (void)c; (void)fp;
    (void)fputc_slow;        /* используется из asm */
    __asm
        ;; HL = c, DE = fp.
#ifndef LIBC_NOCHECK
        ld    a, d
        or    a, e
        jr    Z, _fp_slow           ; NULL — C-путь выставит errno
#endif
        push  de
        pop   iy                    ; IY = fp

        ld    a, 2 (iy)             ; flags
        and   a, #0x08              ; _F_DIROUT — уже копим запись?
        jr    Z, _fp_slow           ; нет (или консоль/чтение) — C-путь
        ld    a, 8 (iy)             ; level_hi < 2  <=>  level < 512
        cp    a, #2
        jr    NC, _fp_slow          ; буфер полон — сброс в C-пути

        ;; --- горячий путь: *curp++ = c, level++ ---
        ld    e, 5 (iy)             ; DE = curp (fp больше не нужен в DE)
        ld    d, 6 (iy)
        ld    a, l
        ld    (de), a
        inc   de
        ld    5 (iy), e
        ld    6 (iy), d
        ld    a, 7 (iy)             ; level++
        inc   a
        ld    7 (iy), a
        jr    NZ, _fp_ret
        inc   8 (iy)
    _fp_ret:
        ld    e, l                  ; вернуть (uint8_t)c
        ld    d, #0
        ret

    _fp_slow:
        jp    _fputc_slow           ; HL = c, DE = fp уже на месте
    __endasm;
}
