#pragma once

#include <psemek/io/stream.hpp>

#include <string_view>

namespace psemek::io
{

	struct memory_istream
		: istream
	{
		memory_istream() = default;

		memory_istream(std::string_view str)
			: begin_{str.data()}
			, end_{str.data() + str.size()}
		{}

		memory_istream(char const * begin, char const * end)
			: begin_{begin}
			, end_{end}
		{}

		memory_istream(memory_istream && s)
			: begin_{s.begin_}
			, end_{s.end_}
		{
			s.reset();
		}

		memory_istream & operator = (memory_istream && s)
		{
			if (this != &s)
			{
				begin_ = s.begin_;
				end_ = s.end_;
				s.reset();
			}

			return *this;
		}

		void reset()
		{
			begin_ = nullptr;
			end_ = nullptr;
		}

		void swap(memory_istream & s)
		{
			std::swap(begin_, s.begin_);
			std::swap(end_, s.end_);
		}

		std::size_t read(char * p, std::size_t size) override;

		bool finished() const override
		{
			return begin_ == end_;
		}

		void advance(std::size_t n)
		{
			if (end_ - begin_ < n)
				throw io::istream_end{};
			begin_ += n;
		}

		char const * data() const { return begin_; }

	private:
		char const * begin_ = nullptr;
		char const * end_ = nullptr;
	};

	struct memory_ostream
		: ostream
	{
		memory_ostream() = default;

		memory_ostream(char * begin, char * end)
			: begin_{begin}
			, end_{end}
		{}

		memory_ostream(memory_ostream && s)
			: begin_{s.begin_}
			, end_{s.end_}
		{
			s.reset();
		}

		memory_ostream & operator = (memory_ostream && s)
		{
			if (this != &s)
			{
				begin_ = s.begin_;
				end_ = s.end_;
				s.reset();
			}

			return *this;
		}

		void reset()
		{
			begin_ = nullptr;
			end_ = nullptr;
		}

		void swap(memory_ostream & s)
		{
			std::swap(begin_, s.begin_);
			std::swap(end_, s.end_);
		}

		void advance(std::size_t n)
		{
			if (end_ - begin_ < n)
				throw io::istream_end{};
			begin_ += n;
		}

		char * data() const { return begin_; }

		std::size_t write(char const * p, std::size_t size) override;

	private:
		char * begin_ = nullptr;
		char * end_ = nullptr;
	};

}
