#pragma once

#include "common.h"
#include "stream.h"

struct OpenMode
{
	typedef uint32_t Flags;

	static const Flags kREAD          = (1u << 0);
	static const Flags kWRITE         = (1u << 1);
	static const Flags kAPPEND        = (1u << 2);
	static const Flags kRECREATE      = (1u << 3);
	static const Flags kSHARE         = (1u << 4);
	static const Flags kRANDOM_ACCESS = (1u << 5);
	static const Flags kWRITE_THROUGH = (1u << 6);
	static const Flags kASYNC         = (1u << 7);
};

class File
	: public IStream
{
public:
	File();
	File(const char* const file_path, const OpenMode::Flags mode);
	~File();

	bool open(const char* const file_path, const OpenMode::Flags mode);
	void close();

	bool read(void* const data, const uint32_t size, uint32_t* const num_read = nullptr);
	bool write(const void* const data, const uint32_t size, uint32_t* const num_written = nullptr);

	inline bool read_at(void* const data, const uint64_t offset, const uint32_t size, uint32_t* const num_read = nullptr)
	{
		uint64_t pos = tell();
		if (!seek(offset, SeekMode::kBEGIN))
			return false;
		const bool result = read(data, size, num_read);
		if (!seek(pos, SeekMode::kBEGIN))
			return false;
		return result;
	}

	inline bool write_at(const void* const data, const uint64_t offset, const uint32_t size, uint32_t* const num_written = nullptr)
	{
		uint64_t pos = tell();
		if (!seek(offset, SeekMode::kBEGIN))
			return false;
		const bool result = write(data, size, num_written);
		if (!seek(pos, SeekMode::kBEGIN))
			return false;
		return result;
	}

	bool seek(const uint64_t offset, const SeekMode::Enum mode = SeekMode::kBEGIN, uint64_t* const position = nullptr);

	uint64_t file_size() const;

	bool is_valid() const;

	inline operator bool() const
	{
		return is_valid();
	}

private:
	class Impl;
	std::unique_ptr<Impl> impl_;
};

bool read_file(const char* const file_path, char** const data, uint32_t* const size, const uint64_t offset = 0);
bool write_file(const char* const file_path, const char* const data, const uint32_t size, const uint64_t offset = 0);
