/*
 * fgets — прочитать строку (до n-1 символов, '\n' сохраняется) из
 * потока в buf с завершающим NUL.  Возвращает buf или NULL, если EOF
 * наступил до первого сохранённого символа.  n=1 по стандарту —
 * валидный вызов: ничего не читает, возвращает пустую строку.
 *
 * Быстрый путь: пока строка лежит в readahead-буфере, она копируется
 * блоком asm-сканером (LDI + сравнение с '\n', ~50 тактов/байт) —
 * fgetc-на-символ был в ~10 раз медленнее (замер fbench).  Медленные
 * случаи (консоль, hold от ungetc, режим записи, дозаполнение буфера,
 * EOF) уходят посимвольно через fgetc.
 */

#include "_file.h"

/* Копировать из src в dst до max байт, остановившись ПОСЛЕ '\n'.
 * Возврат: число скопированных байт | 0x8000, если '\n' найден.
 * max: 1..512 (бит 15 счётчика свободен). */
static uint16_t fgets_scan(uint8_t *dst, const uint8_t *src,
                           uint16_t max) __naked
{
    (void)dst; (void)src; (void)max;
    __asm
        ;; HL = dst, DE = src, max на стеке (callee чистит).
        pop   iy                    ; адрес возврата
        pop   bc                    ; BC = max (> 0)
        ex    de, hl                ; HL = src, DE = dst
        push  bc                    ; max понадобится для подсчёта
    _fgs_loop:
        ld    a, (hl)
        cp    a, #0x0A              ; сравнить ДО ldi: LDI не трогает Z,
        ldi                         ; но выставляет P/V по BC ((DE)<-(HL))
        jr    Z, _fgs_found         ; Z — от cp
        jp    PE, _fgs_loop         ; P/V — от LDI: BC != 0
        ;; max исчерпан, '\n' нет: count = max
        pop   de
        jp    (iy)
    _fgs_found:
        ;; count = max - BC, бит 15 = флаг '\n' (count <= 512 — безопасно)
        pop   hl                    ; HL = max
        ld    a, l
        sub   a, c
        ld    e, a
        ld    a, h
        sbc   a, b
        or    a, #0x80
        ld    d, a
        jp    (iy)
    __endasm;
}

char *fgets(char *buf, int n, FILE *fp)
{
#ifndef LIBC_NOCHECK
    if (!buf || n < 1 || !fp) return NULL;
#endif
    uint8_t *dst = (uint8_t *)buf;
    int space = n - 1;

    while (space > 0) {
        if (fp->level && !(fp->flags & _F_DIROUT) && fp->hold == EOF) {
            /* Быстрый блок: сканировать readahead до '\n'. */
            uint16_t take = fp->level;
            if (take > (uint16_t)space) take = (uint16_t)space;
            uint16_t got = fgets_scan(dst, fp->curp, take);
            uint16_t cnt = got & 0x7FFF;
            fp->curp  += cnt;
            fp->level -= cnt;
            dst   += cnt;
            space -= (int)cnt;
            if (got & 0x8000) break;         /* строка закончена */
            continue;                        /* буфер иссяк — дозаполнить */
        }
        /* Медленные случаи + refill/EOF — посимвольно. */
        int c = fgetc(fp);
        if (c == EOF) {
            if (dst == (uint8_t *)buf) return NULL;
            break;
        }
        *dst++ = (uint8_t)c;
        space--;
        if (c == '\n') break;
    }
    *dst = '\0';
    return buf;
}
