#pragma once

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

// TODO:
// check boundaries & buffer availability

class MemoryFile
	: public IStream
{
public:
	MemoryFile()
		: buffer_(nullptr)
		, current_(nullptr)
		, size_(0)
	{
	}

	MemoryFile(void* const buffer, const uint64_t size)
		: buffer_(buffer)
		, current_(buffer_)
		, size_(size)
	{
	}

	~MemoryFile()
	{
	}

	bool read(void* const data, const uint32_t size, uint32_t* const num_read = nullptr)
	{
		// TODO: boundary check
		memcpy(data, current_, size);
		if (num_read)
			*num_read = size;
		current_ = static_cast<char*>(current_) + size;
		return true;
	}

	bool write(const void* const data, const uint32_t size, uint32_t* const num_written = nullptr)
	{
		// TODO: boundary check
		memcpy(current_, data, size);
		if (num_written)
			*num_written = size;
		current_ = static_cast<char*>(current_) + size;
		return true;
	}

	bool seek(const uint64_t offset, const SeekMode::Enum mode = SeekMode::kBEGIN, uint64_t* const position = nullptr)
	{
		// TODO: boundary check
		switch (mode) {
			case SeekMode::kBEGIN: current_ = static_cast<char*>(buffer_) + offset; break;
			case SeekMode::kCURRENT: current_ = static_cast<char*>(current_) + offset; break;
			case SeekMode::kEND: current_ = static_cast<char*>(buffer_) + size_ + offset; break;
			default:
				assert(0 && "Invalid mode specified.");
				return false;
		}
		if (position)
			*position = static_cast<char*>(current_) - static_cast<char*>(buffer_);
		return true;
	}

	uint64_t file_size() const
	{
		return size_;
	}

	bool is_valid() const
	{
		return buffer_ != nullptr;
	}

private:
	void* buffer_;
	void* current_;
	uint64_t size_;
};
