/************************************************************/ /**
*
*	@file: platform_io.h
*	@author: Martin Fouilleul
*	@date: 25/05/2023
*
*****************************************************************/
#ifndef __PLATFORM_IO_H_
#define __PLATFORM_IO_H_

#include "util/strings.h"
#include "util/typedefs.h"

//----------------------------------------------------------------
// IO API
//----------------------------------------------------------------

typedef struct
{
    u64 h;
} oc_file;

typedef u16 oc_file_open_flags;

enum oc_file_open_flags_enum
{
    OC_FILE_OPEN_NONE = 0,
    OC_FILE_OPEN_APPEND = 1 << 1,
    OC_FILE_OPEN_TRUNCATE = 1 << 2,
    OC_FILE_OPEN_CREATE = 1 << 3,

    OC_FILE_OPEN_SYMLINK = 1 << 4,
    OC_FILE_OPEN_NO_FOLLOW = 1 << 5,
    OC_FILE_OPEN_RESTRICT = 1 << 6,
    //...
};

typedef u16 oc_file_access;

enum oc_file_access_enum
{
    OC_FILE_ACCESS_NONE = 0,
    OC_FILE_ACCESS_READ = 1 << 1,
    OC_FILE_ACCESS_WRITE = 1 << 2,
};

typedef enum
{
    OC_FILE_SEEK_SET,
    OC_FILE_SEEK_END,
    OC_FILE_SEEK_CURRENT
} oc_file_whence;

typedef u64 oc_io_req_id;

typedef u32 oc_io_op;

enum oc_io_op_enum
{
    OC_IO_OPEN_AT = 0,
    OC_IO_CLOSE,

    OC_IO_FSTAT,

    OC_IO_SEEK,
    OC_IO_READ,
    OC_IO_WRITE,

    OC_OC_IO_ERROR,
    //...
};

typedef struct oc_io_req
{
    oc_io_req_id id;
    oc_io_op op;
    oc_file handle;

    i64 offset;
    u64 size;

    union
    {
        char* buffer;
        u64 unused; // This is a horrible hack to get the same layout on wasm and on host
    };

    union
    {
        struct
        {
            oc_file_access rights;
            oc_file_open_flags flags;
        } open;

        oc_file_whence whence;
    };

} oc_io_req;

typedef i32 oc_io_error;

enum oc_io_error_enum
{
    OC_IO_OK = 0,
    OC_IO_ERR_UNKNOWN,
    OC_IO_ERR_OP,          // unsupported operation
    OC_IO_ERR_HANDLE,      // invalid handle
    OC_IO_ERR_PREV,        // previously had a fatal error (last error stored on handle)
    OC_IO_ERR_ARG,         // invalid argument or argument combination
    OC_IO_ERR_PERM,        // access denied
    OC_IO_ERR_SPACE,       // no space left
    OC_IO_ERR_NO_ENTRY,    // file or directory does not exist
    OC_IO_ERR_EXISTS,      // file already exists
    OC_IO_ERR_NOT_DIR,     // path element is not a directory
    OC_IO_ERR_DIR,         // attempted to write directory
    OC_IO_ERR_MAX_FILES,   // max open files reached
    OC_IO_ERR_MAX_LINKS,   // too many symbolic links in path
    OC_IO_ERR_PATH_LENGTH, // path too long
    OC_IO_ERR_FILE_SIZE,   // file too big
    OC_IO_ERR_OVERFLOW,    // offset too big
    OC_IO_ERR_NOT_READY,   // no data ready to be read/written
    OC_IO_ERR_MEM,         // failed to allocate memory
    OC_IO_ERR_INTERRUPT,   // operation interrupted by a signal
    OC_IO_ERR_PHYSICAL,    // physical IO error
    OC_IO_ERR_NO_DEVICE,   // device not found
    OC_IO_ERR_WALKOUT,     // attempted to walk out of root directory

    //...
};

typedef struct oc_io_cmp
{
    oc_io_req_id id;
    oc_io_error error;

    union
    {
        i64 result;
        u64 size;
        i64 offset;
        oc_file handle;
        //...
    };
} oc_io_cmp;

//----------------------------------------------------------------
//TODO: complete io queue api
//----------------------------------------------------------------
ORCA_API oc_io_cmp oc_io_wait_single_req(oc_io_req* req);

//----------------------------------------------------------------
// File IO wrapper API
//----------------------------------------------------------------

ORCA_API oc_file oc_file_nil();
ORCA_API bool oc_file_is_nil(oc_file handle);

ORCA_API oc_file oc_file_open(oc_str8 path, oc_file_access rights, oc_file_open_flags flags);
ORCA_API oc_file oc_file_open_at(oc_file dir, oc_str8 path, oc_file_access rights, oc_file_open_flags flags);
ORCA_API void oc_file_close(oc_file file);

ORCA_API i64 oc_file_pos(oc_file file);
ORCA_API i64 oc_file_seek(oc_file file, i64 offset, oc_file_whence whence);

ORCA_API u64 oc_file_write(oc_file file, u64 size, char* buffer);
ORCA_API u64 oc_file_read(oc_file file, u64 size, char* buffer);

ORCA_API oc_io_error oc_file_last_error(oc_file handle);

//----------------------------------------------------------------
// File System wrapper API
//----------------------------------------------------------------

typedef enum oc_file_type
{
    OC_FILE_UNKNOWN,
    OC_FILE_REGULAR,
    OC_FILE_DIRECTORY,
    OC_FILE_SYMLINK,
    OC_FILE_BLOCK,
    OC_FILE_CHARACTER,
    OC_FILE_FIFO,
    OC_FILE_SOCKET,

} oc_file_type;

typedef u16 oc_file_perm;

enum oc_file_perm
{
    OC_FILE_OTHER_EXEC = 1 << 0,
    OC_FILE_OTHER_WRITE = 1 << 1,
    OC_FILE_OTHER_READ = 1 << 2,

    OC_FILE_GROUP_EXEC = 1 << 3,
    OC_FILE_GROUP_WRITE = 1 << 4,
    OC_FILE_GROUP_READ = 1 << 5,

    OC_FILE_OWNER_EXEC = 1 << 6,
    OC_FILE_OWNER_WRITE = 1 << 7,
    OC_FILE_OWNER_READ = 1 << 8,

    OC_FILE_STICKY_BIT = 1 << 9,
    OC_FILE_SET_GID = 1 << 10,
    OC_FILE_SET_UID = 1 << 11,
};

typedef struct oc_file_status
{
    u64 uid;
    oc_file_type type;
    oc_file_perm perm;
    u64 size;

    //TODO times

} oc_file_status;

ORCA_API oc_file_status oc_file_get_status(oc_file file);
ORCA_API u64 oc_file_size(oc_file file);

//TODO: Complete as needed...

#endif //__PLATFORM_IO_H_
