/*
 * fread — читает nmemb элементов по size байт из потока в ptr
 * (буферизованно, вариант B+).
 *
 * Порядок: hold-байт → хвост буфера (memcpy) → большой остаток
 * (>= BUFSIZ) одним read() напрямую в ptr мимо буфера → мелкий
 * остаток через дозаполнение буфера.  Возвращает число ПОЛНЫХ
 * элементов; короткое чтение ставит _F_EOF, ошибка — _F_ERROR.
 * Консольный поток — посимвольно через getchar().
 */

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

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *fp)
{
#ifndef LIBC_NOCHECK
    if (!ptr || !fp) return 0;
#endif
    if (size == 0 || nmemb == 0) return 0;

    uint8_t *p    = (uint8_t *)ptr;
    uint16_t total = size * nmemb;
    uint16_t got   = 0;
    uint16_t n;
    int r;

    if (fp->hold != EOF) {
        *p++ = (uint8_t)fp->hold;
        fp->hold = EOF;
        got = 1;
    }
    if (fp->flags & _F_CONIN) {
        while (got < total) {
            int c = getchar();
            if (c == EOF) break;
            *p++ = (uint8_t)c;
            got++;
        }
        goto done;
    }
#ifndef LIBC_NOCHECK
    if (!(fp->flags & _F_READ)) { errno = EBADF; return 0; }
#endif
    if (fp->flags & _F_DIROUT) {
        if (_file_sync(fp)) return 0;      /* сброс накопленной записи */
    }

    /* 1) хвост readahead-буфера */
    if (fp->level) {
        n = total - got;
        if (n > fp->level) n = fp->level;
        memcpy(p, fp->curp, n);
        fp->curp  += n;
        fp->level -= n;
        p   += n;
        got += n;
    }

    n = total - got;
    if (n == 0) goto done;
    if (n >= BUFSIZ || !_file_buf(fp)) {
        /* 2а) большой остаток (или нет буфера) — напрямую мимо буфера */
        r = read(fp->fd, p, n);
    } else {
        /* 2б) мелкий остаток — дозаполнить буфер и скопировать */
        r = read(fp->fd, fp->buf, BUFSIZ);
        if (r >= 0) {
            fp->level = (uint16_t)r;
            fp->curp  = fp->buf;
            if ((uint16_t)r < n) n = (uint16_t)r;
            memcpy(p, fp->curp, n);
            fp->curp  += n;
            fp->level -= n;
            r = (int)n;
        }
    }
    if (r < 0) { fp->flags |= _F_ERROR; goto done; }
    got += (uint16_t)r;
    if (got < total) fp->flags |= _F_EOF;

done:
    return size == 1 ? got : got / size;
}
