/*
 * fwrite — пишет nmemb элементов по size байт из ptr в поток
 * (буферизованно, вариант B+).
 *
 * Большой блок (>= BUFSIZ, или нет буфера): сброс накопленного и один
 * write() напрямую из ptr мимо буфера.  Мелкий: memcpy в буфер (со
 * сбросом ПЕРЕД записью, если не влезает).  Если поток был в режиме
 * чтения — _file_sync откатит readahead.  Возвращает число ПОЛНЫХ
 * записанных элементов; ошибка/короткая запись ставит _F_ERROR.
 * Ошибки буферизованной части могут вылезти позже (fflush/fclose).
 * Консольный поток — посимвольно через putchar().
 */

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

size_t fwrite(const 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;

    const uint8_t *p = (const uint8_t *)ptr;
    size_t total = size * nmemb;

    if (fp->flags & _F_CONOUT) {
        for (size_t i = 0; i < total; i++) {
            putchar(p[i]);
        }
        return nmemb;
    }
#ifndef LIBC_NOCHECK
    if (!(fp->flags & _F_WRITE)) { errno = EBADF; return 0; }
#endif

    if (!(fp->flags & _F_DIROUT)) {
        if (_file_sync(fp)) return 0;        /* откат readahead/hold */
    }

    if (total >= BUFSIZ || !_file_buf(fp)) {
        /* Большой блок (или нет буфера): сброс хвоста + прямой write. */
        if (_file_sync(fp)) return 0;
        int w = write(fp->fd, p, (uint16_t)total);
        if (w < 0) { fp->flags |= _F_ERROR; return 0; }
        if ((size_t)w < total) fp->flags |= _F_ERROR;
        return (size_t)w / size;
    }

    /* Мелкий блок — в буфер; сброс ПЕРЕД записью, если не влезает. */
    if (fp->level + total > BUFSIZ) {
        if (_file_sync(fp)) return 0;
    }
    if (!(fp->flags & _F_DIROUT)) {
        fp->flags |= _F_DIROUT;
        fp->curp   = fp->buf;
    }
    memcpy(fp->curp, p, total);
    fp->curp  += total;
    fp->level += (uint16_t)total;
    return nmemb;
}
