#pragma once
#include <bits/open-flags.h>
#include <luna/Buffer.h>
#include <luna/Result.h>
#include <luna/SharedPtr.h>
#include <luna/StringView.h>
#include <os/Path.h>
#include <sys/types.h>

namespace os
{
    class File
    {
      public:
        enum OpenMode
        {
            ReadOnly = O_RDONLY,
            WriteOnly = O_WRONLY | O_TRUNC,
            ReadWrite = O_RDWR | O_TRUNC,
            Append = O_WRONLY | O_APPEND,
            ReadAppend = O_RDWR | O_APPEND,
        };

        static Result<SharedPtr<File>> open(const Path& path, OpenMode flags);
        static Result<SharedPtr<File>> open_or_create(const Path& path, OpenMode flags, mode_t mode = 0644);
        static Result<SharedPtr<File>> create(const Path& path, OpenMode flags, mode_t mode = 0644);

        /*
          If path is "-", return standard input (as is common for many CLI apps). Otherwise, open path for reading.

          This function is a convenience function for CLI apps, so that they don't need to check path and open standard
          input if necessary.
        */
        static Result<SharedPtr<File>> open_input_file(StringView path);

        static SharedPtr<File> standard_input();
        static SharedPtr<File> standard_output();
        static SharedPtr<File> standard_error();

        void set_close_on_exec();

        Result<void> write(StringView str);
        Result<void> write(const Buffer& buf);

        Result<String> read_line();
        Result<String> read_all_as_string();

        Result<void> read(Buffer& buf, usize size);
        Result<Buffer> read_all();

        Result<int> getchar();

        int fd() const
        {
            return m_fd;
        }

        File(Badge<File>);
        ~File();

      private:
        static Result<SharedPtr<File>> construct(const Path& path, int flags, mode_t mode);
        static void initialize_standard_streams();

        Result<usize> raw_read(u8* buf, usize length);
        Result<usize> raw_write(const u8* buf, usize length);

        int m_fd { -1 };
    };

    Result<void> print(StringView fmt, ...);
    Result<void> println(StringView fmt, ...);
    Result<void> eprint(StringView fmt, ...);
    Result<void> eprintln(StringView fmt, ...);
}