#pragma once

#include <psemek/sir/stream.hpp>
#include <psemek/sir/trivial.hpp>
#include <psemek/sir/container.hpp>
#include <psemek/io/memory_stream.hpp>
#include <psemek/util/at.hpp>

namespace psemek::sir
{

	struct memory_istream
		: istream
	{
		memory_istream(io::memory_istream & s, std::size_t offset = 0)
			: istream(s, offset)
		{}

		char const * data() const
		{
			return static_cast<io::memory_istream const &>(stream()).data();
		}

		void advance(std::size_t n)
		{
			static_cast<io::memory_istream &>(stream()).advance(n);
		}

	private:
		std::string_view data_;
	};

	static_assert(is_istream_v<memory_istream>);

	namespace detail
	{

		template <typename T>
		struct vector
		{
			vector() = default;

			vector(T const * begin, T const * end)
				: begin_(begin)
				, end_(end)
			{}

			vector(vector && v)
				: begin_(v.begin_)
				, end_(v.end_)
			{
				v.begin_ = nullptr;
				v.end_ = nullptr;
			}

			vector(vector const &) = default;

			vector & operator = (vector const &) = default;

			vector & operator = (vector && v)
			{
				if (&v == this)
					return *this;

				begin_ = v.begin_;
				end_ = v.end_;

				v.begin_ = nullptr;
				v.end_ = nullptr;
				return *this;
			}

			T const * begin() const
			{
				return begin_;
			}

			T const * end() const
			{
				return end_;
			}

			std::size_t size() const
			{
				return end_ - begin_;
			}

			T const & operator[] (std::size_t i) const
			{
				return begin_[i];
			}

			T const & at(std::size_t i) const
			{
				if (i >= size())
					throw util::key_error(i);
				return begin_[i];
			}

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

		template <typename T>
		requires is_trivial_v<T>
		void read(memory_istream & s, vector<T> & x)
		{
			size_type size;
			read(s, size);
			read_padding<T>(s);
			auto begin = reinterpret_cast<T const *>(s.data());
			s.advance(size * sizeof(T));
			auto end = reinterpret_cast<T const *>(s.data());
			x = vector<T>(begin, end);
		}

		template <typename T, typename Compare = std::less<void>>
		struct set
		{
			set() = default;

			set(T const * begin, T const * end)
				: begin_(begin)
				, end_(end)
			{}

			set(set && s)
				: begin_(s.begin_)
				, end_(s.end_)
			{
				s.begin_ = nullptr;
				s.end_ = nullptr;
			}

			set(set const &) = default;

			set & operator = (set const &) = default;

			set & operator = (set && s)
			{
				if (&s == this)
					return *this;

				begin_ = s.begin_;
				end_ = s.end_;

				s.begin_ = nullptr;
				s.end_ = nullptr;
				return *this;
			}

			T const * begin() const
			{
				return begin_;
			}

			T const * end() const
			{
				return end_;
			}

			std::size_t size() const
			{
				return end_ - begin_;
			}

			template <typename H>
			T const * find(H const & x) const
			{
				Compare comp;
				auto it = std::lower_bound(begin_, end_, x, comp);
				if (it != end_ && comp(x, *it))
					return end_;
				return it;
			}

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

		template <typename T, typename Compare>
		requires is_trivial_v<T>
		void read(memory_istream & s, set<T, Compare> & x)
		{
			size_type size;
			read(s, size);
			read_padding<T>(s);
			auto begin = reinterpret_cast<T const *>(s.data());
			s.advance(size * sizeof(T));
			auto end = reinterpret_cast<T const *>(s.data());
			x = set<T, Compare>(begin, end);
		}

		template <typename K, typename V, typename Compare = std::less<void>>
		struct map
		{
			struct pair
			{
				K first;
				V second;
			};

			map() = default;

			map(pair const * begin, pair const * end)
				: begin_(begin)
				, end_(end)
			{}

			map(map && m)
				: begin_(m.begin_)
				, end_(m.end_)
			{
				m.begin_ = nullptr;
				m.end_ = nullptr;
			}

			map(map const &) = default;

			map & operator = (map const &) = default;

			map & operator = (map && m)
			{
				if (&m == this)
					return *this;

				begin_ = m.begin_;
				end_ = m.end_;

				m.begin_ = nullptr;
				m.end_ = nullptr;
				return *this;
			}

			pair const * begin() const
			{
				return begin_;
			}

			pair const * end() const
			{
				return end_;
			}

			std::size_t size() const
			{
				return end_ - begin_;
			}

			template <typename H>
			pair const * find(H const & x) const
			{
				Compare comp;
				auto it = std::lower_bound(begin_, end_, x, [&](auto const & p, auto const & x){ return comp(p.first, x); });
				if (it != end_ && comp(x, it->first))
					return end_;
				return it;
			}

			V const & at(K const & key) const
			{
				auto p = find(key);
				if (p == end_)
					throw util::key_error(key);
				return p->second;
			}

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

		template <typename K, typename V, typename Compare>
		requires is_trivial_v<typename map<K, V, Compare>::pair>
		void read(memory_istream & s, map<K, V, Compare> & x)
		{
			using T = typename map<K, V, Compare>::pair;
			size_type size;
			read(s, size);
			read_padding<T>(s);
			auto begin = reinterpret_cast<T const *>(s.data());
			s.advance(size * sizeof(T));
			auto end = reinterpret_cast<T const *>(s.data());
			x = map<K, V, Compare>(begin, end);
		}

	}

	using detail::vector;
	using detail::set;
	using detail::map;

}
