/* stdio.h: Standard input/output. */

#ifndef _STDIO_H
#define _STDIO_H

#include <bits/seek.h>
#include <stdarg.h>
#include <sys/types.h>

#define __need_NULL
#include <stddef.h>

#define FOPEN_MAX 64 // Make sure this value matches FD_MAX in the kernel source.

typedef struct
{
    int _fd;  // The underlying file descriptor.
    int _err; // The error status flag.
    int _eof; // The end-of-file status flag.
    struct
    {
        size_t capacity; // The buffer's total capacity.
        size_t size;     // The buffer's used size.
        size_t index;    // The read index into the buffer.
        char* buffer;    // The memory used for the buffer.
        int status;      // The buffer status flags.
        int mode;        // The buffering mode.
    } _buf;
    int _flags; // The file access mode with which the file was opened.
    pid_t _pid; // For popen(3) files, the pid of the child process.
} FILE;

#define EOF -1

extern FILE* stdin;
extern FILE* stdout;
extern FILE* stderr;
#define stdin stdin
#define stdout stdout
#define stderr stderr

#define BUFSIZ 4096
#define FILENAME_MAX                                                                                                   \
    1024 // As Luna does not impose a limit on this, this is the recommended size for character arrays holding a file
         // name.

#define _IONBF 0
#define _IOLBF 1
#define _IOFBF 2

#ifdef __cplusplus
extern "C"
{
#endif

    /* Flush a stream's buffers. */
    int fflush(FILE*);

    /* Open a file and bind a stream to it. */
    FILE* fopen(const char* path, const char* mode);

    /* Bind a stream to a file descriptor. */
    FILE* fdopen(int fd, const char* mode);

    /* Change the underlying file and mode of a stream. */
    FILE* freopen(const char* path, const char* mode, FILE* stream);

    /* Close a file and frees up its stream. */
    int fclose(FILE* stream);

    /* Return the file descriptor associated with a stream. */
    int fileno(FILE* stream);

    /* Read arbitrarily sized items from a stream. */
    size_t fread(void* buf, size_t size, size_t nmemb, FILE* stream);

    /* Write arbitrarily sized items to a stream. */
    size_t fwrite(const void* buf, size_t size, size_t nmemb, FILE* stream);

    /* Move the file position. Clears the end-of-file indicator on success. */
    int fseek(FILE* stream, long offset, int whence);

    /* Return the current file position. */
    long ftell(FILE* stream);

    /* Rewind the file position and clear the error and end-of-file indicators. */
    void rewind(FILE* stream);

    /* Save the current file position. */
    int fgetpos(FILE* stream, fpos_t* pos);

    /* Restore a file position. */
    int fsetpos(FILE* stream, const fpos_t* pos);

    /* Return whether the error indicator was set in stream. */
    int ferror(FILE* stream);

    /* Return whether the end-of-file indicator was set in stream. */
    int feof(FILE* stream);

    /* Write a character to stream. */
    int fputc(int c, FILE* stream);

    /* Write a character to stream. */
    int putc(int c, FILE* stream);

    /* Write a character to standard output. */
    int putchar(int c);

    /* Write a string to stream. */
    int fputs(const char* str, FILE* stream);

    /* Read a character from stream. */
    int fgetc(FILE* stream);

    /* Read a character from stream. */
    int getc(FILE* stream);

    /* Read a character from standard input. */
    int getchar(void);

    /* Push a character back to stream so that it can be read again. */
    int ungetc(int c, FILE* stream);

    /* Read a line from stream. */
    char* fgets(char* buf, size_t size, FILE* stream);

    /* Read a line from stream and store it in a dynamically-allocated buffer. */
    ssize_t getline(char** linep, size_t* n, FILE* stream);

    /* Read a line from stream and store it in a dynamically-allocated buffer. */
    ssize_t getdelim(char** linep, size_t* n, int delim, FILE* stream);

    /* Clear the error and end-of-file indicators in stream. */
    void clearerr(FILE* stream);

    /* Write formatted output to a file. */
    int fprintf(FILE* stream, const char* format, ...);

    /* Write formatted output to a file. */
    int vfprintf(FILE* stream, const char* format, va_list ap);

    /* Write formatted output into a buffer. */
    int sprintf(char* buf, const char* format, ...);

    /* Write up to max bytes of formatted output into a buffer. */
    int snprintf(char* buf, size_t max, const char* format, ...);

    /* Write formatted output into a buffer. */
    int vsprintf(char* buf, const char* format, va_list ap);

    /* Write up to max bytes of formatted output into a buffer. */
    int vsnprintf(char* buf, size_t max, const char* format, va_list ap);

    /* Write formatted output to standard output. */
    int vprintf(const char* format, va_list ap);

    /* Write formatted output to standard output. */
    int printf(const char* format, ...);

    /* Scan formatted input from a string. */
    int vsscanf(const char* str, const char* format, va_list ap);

    /* Scan formatted input from a string. */
    int sscanf(const char* str, const char* format, ...);

    /* Scan formatted input from a file. */
    int vfscanf(FILE* stream, const char* format, va_list ap);

    /* Scan formatted input from a file. */
    int fscanf(FILE* stream, const char* format, ...);

    /* Scan formatted input from standard input. */
    int vscanf(const char* format, va_list ap);

    /* Scan formatted input from standard input. */
    int scanf(const char* format, ...);

    /* Write a string followed by a newline to standard output. */
    int puts(const char* s);

    /* Write an error message to standard error. */
    void perror(const char* s);

    /* Remove a file from the filesystem. */
    int remove(const char* path);

    /* Create a unique temporary file. */
    FILE* tmpfile(void);

    /* Change a file's buffering mode and internal buffer. */
    int setvbuf(FILE* stream, char* buf, int mode, size_t size);

    /* Change a file's internal buffer. */
    void setbuf(FILE* stream, char* buf);

    /* Change a file's internal buffer. */
    void setbuffer(FILE* stream, char* buf, size_t size);

    /* Change a file's buffering mode to line buffered. */
    void setlinebuf(FILE* stream);

    /* Move a file's location across a file system. */
    int rename(const char* oldpath, const char* newpath);

    /* Pipe a stream to or from a process. */
    FILE* popen(const char* command, const char* type);

#ifdef __cplusplus
}
#endif

#endif