#pragma once

#include <Core/Common/NonNullPtr.h>
#include <Core/Common/String.h>
#include <Juliet.h>

namespace Juliet
{
    // Opaque type
    struct IOStream;

    struct IOStreamDataPayload
    {
    };

    enum class IOStreamStatus : uint8
    {
        Ready,
        Error,
        EndOfFile,
        NotReady,
        ReadOnly,
        WriteOnly
    };

    enum class IOStreamSeekPivot : uint8
    {
        Begin,
        Current,
        End,
        Count
    };

    // IOStream can be opened on a file or memory, or anything else.
    // Use the interface to make it transparent to the user.
    struct IOStreamInterface
    {
        uint32 Version;

        int64 (*Size)(NonNullPtr<IOStreamDataPayload> data);

        int64 (*Seek)(NonNullPtr<IOStreamDataPayload> data, int64 offset, IOStreamSeekPivot pivot);
        size_t (*Read)(NonNullPtr<IOStreamDataPayload> data, void* outBuffer, size_t size, NonNullPtr<IOStreamStatus> status);
        size_t (*Write)(NonNullPtr<IOStreamDataPayload> data, ByteBuffer inBuffer, NonNullPtr<IOStreamStatus> status);
        bool (*Flush)(NonNullPtr<IOStreamDataPayload> data, NonNullPtr<IOStreamStatus> status);

        bool (*Close)(NonNullPtr<IOStreamDataPayload> data);
    };

    extern JULIET_API IOStream* IOFromFile(String filename, String mode);

    // Let you use an interface to open any io. Is used internally by IOFromFile
    extern JULIET_API IOStream* IOFromInterface(NonNullPtr<const IOStreamInterface> streamInterface, NonNullPtr<IOStreamDataPayload> payload);

    // Write formatted string into the stream.
    extern JULIET_API size_t IOPrintf(NonNullPtr<IOStream> stream, _Printf_format_string_ const char* format, ...);
    extern JULIET_API size_t IOWrite(NonNullPtr<IOStream> stream, ByteBuffer inBuffer);

    extern JULIET_API size_t IORead(NonNullPtr<IOStream> stream, void* ptr, size_t size);
    extern JULIET_API int64  IOSeek(NonNullPtr<IOStream> stream, int64 offset, IOStreamSeekPivot pivot);

    extern JULIET_API int64 IOSize(NonNullPtr<IOStream> stream);

    // TODO : Use memory arena because that Allocates
    extern JULIET_API ByteBuffer LoadFile(String filename);
    extern JULIET_API ByteBuffer LoadFile(NonNullPtr<IOStream> stream, bool closeStreamWhenDone);

    extern JULIET_API bool IOClose(NonNullPtr<IOStream> stream);
} // namespace Juliet
