/*
 * stdio.h — extends SDCC's z80 stdio with a buffered FILE * stream API.
 *
 * The bottom of the stack is the existing POSIX-style fd I/O (open/
 * read/write/close/lseek).  FILE * adds a per-stream 512-byte buffer,
 * lazily malloc'ed on first buffered operation, shared between reading
 * (readahead) and writing (accumulation) with automatic direction
 * switching — see docs/file-buffering-design.md (variant B+).
 *
 * FILE objects live in a static table of OPEN_MAX slots (no malloc);
 * a slot is free when flags == 0.  exit() flushes and closes all open
 * streams via an atexit-registered _fclosall (armed by first fopen).
 *
 * stdin / stdout / stderr are predefined "virtual" FILE * pointers
 * that talk to the console via putchar()/getchar() (NOT through a fd)
 * and are never buffered.  That keeps printf-routed output going
 * through our CR/LF mapping.
 */

#ifndef STDIO_H
#define STDIO_H

#include <stdarg.h>
#include <stddef.h>      /* size_t */
#include <stdint.h>

#ifndef EOF
#define EOF (-1)
#endif

/* ---- printf family (linked from SDCC's z80.lib) -------------------- */
int  printf (const char *, ...);
int  sprintf(char *, const char *, ...);
int  vprintf(const char *, va_list);
int  vsprintf(char *, const char *, va_list);

/* puts / putchar / getchar — overridden by our libc to use ESTEX. */
char puts   (const char *);
int  putchar(int);
int  getchar(void);

/* ---- FILE * (buffered, variant B+) --------------------------------- */

/* Максимум одновременно открытых fopen-потоков (консольные псевдо-
 * потоки не в счёт).  DSS сам ограничивает число манипуляторов
 * (ошибка 06h "Too many open files"); наша таблица зеркалит его. */
#define OPEN_MAX   8
#define FOPEN_MAX  OPEN_MAX

/* Размер ленивого буфера потока. */
#define BUFSIZ     512

/* Internal layout — opaque to user.  Слот таблицы свободен, когда
 * flags == 0.  level — число байт: непрочитанный readahead (направление
 * «чтение») либо ещё не сброшенная запись (направление «запись»,
 * флаг _F_DIROUT). */
typedef struct __FILE {
    int      fd;         /* POSIX fd (манипулятор DSS) */
    uint8_t  flags;      /* см. _F_* ниже; 0 = слот свободен */
    uint8_t *buf;        /* ленивый буфер BUFSIZ байт или NULL */
    uint8_t *curp;       /* текущая позиция в буфере */
    uint16_t level;      /* байт в буфере (смысл зависит от _F_DIROUT) */
    int      hold;       /* байт от ungetc, EOF = пусто */
} FILE;

#define _F_READ    0x01
#define _F_WRITE   0x02
#define _F_APPEND  0x04
#define _F_DIROUT  0x08  /* буфер сейчас копит запись (иначе readahead) */
#define _F_EOF     0x10
#define _F_ERROR   0x20
#define _F_CONIN   0x40  /* console pseudo-stream — uses getchar() */
#define _F_CONOUT  0x80  /* console pseudo-stream — uses putchar() */

extern FILE *const stdin;
extern FILE *const stdout;
extern FILE *const stderr;

/* fseek whence — same numeric values as SEEK_SET/CUR/END in unistd.h. */
#ifndef SEEK_SET
#define SEEK_SET 0
#define SEEK_CUR 1
#define SEEK_END 2
#endif

/* Тип позиции файла для fgetpos/fsetpos. */
typedef long fpos_t;

FILE  *fopen  (const char *path, const char *mode);
FILE  *fdopen (int fd, const char *mode);
FILE  *freopen(const char *path, const char *mode, FILE *fp);
int    fclose (FILE *fp);
void   fclosall(void);
int    fflush (FILE *fp);          /* fflush(NULL) — все потоки */

int    fputc (int c, FILE *fp);
int    fgetc (FILE *fp);
int    fputs (const char *s, FILE *fp);
char  *fgets (char *buf, int n, FILE *fp);
int    ungetc(int c, FILE *fp);

int    fprintf (FILE *fp, const char *fmt, ...);
int    vfprintf(FILE *fp, const char *fmt, va_list ap);

/* scanf-семейство (семантика Solid-C, реализация своя — в SDCC z80
 * его нет).  Конверсии: %d %u %x %o %c %s (+ модификатор l, ширина,
 * подавление '*', %%).  Возврат: число присвоенных полей, EOF если
 * ввод кончился до первого совпадения. */
int    scanf (const char *fmt, ...);
int    fscanf(FILE *fp, const char *fmt, ...);
int    sscanf(const char *s, const char *fmt, ...);

/* Переименование файла (ESTEX RENAME $10). */
int    rename(const char *oldpath, const char *newpath);

size_t fread (void *ptr, size_t size, size_t nmemb, FILE *fp);
size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *fp);

int    fseek (FILE *fp, long off, int whence);
long   ftell (FILE *fp);
void   rewind(FILE *fp);
int    fgetpos(FILE *fp, fpos_t *pos);
int    fsetpos(FILE *fp, const fpos_t *pos);

/* НЕ inline (замерено 2026-07-10, filetest +56 Б): тело с NULL-проверкой
 * ~12-15 байт — при 2+ сайтах вызова инлайн крупнее call+общее тело. */
int    feof  (FILE *fp);
int    ferror(FILE *fp);
void   clearerr(FILE *fp);

/* Aliases to fputc/fgetc — POSIX says they may be macros. */
#define putc(c, fp)   fputc(c, fp)
#define getc(fp)      fgetc(fp)

/* Read line from stdin into buf until '\n' or EOF; '\n' is not stored.
 * Returns buf, or NULL on EOF with empty input.  Solid-C/POSIX semantic.
 * Note: dangerous, no length check — caller must size buf appropriately. */
char *gets(char *buf);

/* Solid-C decimal/hex helpers — print uint as decimal/hex without args. */
void hex8 (uint8_t  v);   /* prints two hex digits */
void hex16(uint16_t v);   /* prints four hex digits */
void hex32(uint32_t v);   /* prints eight hex digits */
void dec8 (uint8_t  v);   /* prints up to 3 decimal digits, no padding */
void dec16(uint16_t v);   /* prints up to 5 decimal digits */
void dec32(uint32_t v);   /* prints up to 10 decimal digits */

#endif
