#include "storage.h"

namespace ng
{
	namespace detail
	{
		// ============
		// = memory_t =
		// ============

		template <typename _InputIter>
		memory_t::memory_t (_InputIter first, _InputIter last) : _offset(0)
		{
			_helper.reset(new helper_t(std::distance(first, last)));
			std::copy(first, last, _helper->bytes());
		}

		template <typename _InputIter>
		void memory_t::insert (size_t pos, _InputIter first, _InputIter last)
		{
			ASSERT_EQ(_offset + pos, _helper->size());
			std::copy(first, last, _helper->bytes() + _offset + pos);
			_helper->grow(std::distance(first, last));
		}

		// =============
		// = storage_t =
		// =============

		static int comp_abs (size_t pos, size_t const& offset, size_t const& node) { return pos < offset ? -1 : (pos == offset ? 0 : +1); }

		storage_t::tree_t::iterator storage_t::find_pos (size_t pos) const
		{
			auto it = _tree.upper_bound(pos, &comp_abs);
			if(it != _tree.begin())
				--it;
			return it;
		}

		storage_t::tree_t::iterator storage_t::split_at (tree_t::iterator it, size_t pos)
		{
			ASSERT_LE(pos, it->key);

			if(pos == 0)
				return it;
			else if(pos == it->key)
				return ++it;

			size_t suffixLen = it->key - pos;
			memory_t suffix = it->value.subset(pos);

			it->key = pos;
			_tree.update_key(it);

			return _tree.insert(++it, suffixLen, suffix);
		}

		void storage_t::insert (size_t pos, char const* data, size_t length)
		{
			ASSERT_LE(pos, size());
			if(length == 0)
				return;

			auto it = find_pos(pos);
			if(it != _tree.end() && it->offset < pos)
				it = split_at(it, pos - it->offset);

			if(it != _tree.begin())
			{
				auto tmp = it;
				--tmp;
				if(tmp->key == tmp->value.size() && length <= tmp->value.free())
				{
					tmp->value.insert(tmp->key, data, data + length);
					tmp->key += length;
					_tree.update_key(tmp);
					return;
				}
			}

			_tree.insert(it, length, memory_t(data, data + length));
		}

		void storage_t::erase (size_t first, size_t last)
		{
			ASSERT_LE(first, last); ASSERT_LE(last, size());

			auto from = find_pos(first);
			from = from != _tree.end() ? split_at(from, first - from->offset) : from;

			auto to = find_pos(last);
			to = to != _tree.end() ? split_at(to, last - to->offset) : to;

			_tree.erase(from, to);
		}

		char storage_t::operator[] (size_t i) const
		{
			ASSERT_LE(i, size());
			auto it = find_pos(i);
			return it->value.bytes()[i - it->offset];
		}

		std::string storage_t::substr (size_t first, size_t last) const
		{
			ASSERT_LE(first, last); ASSERT_LE(last, size());
			std::string res = "";

			auto from = find_pos(first);
			auto to = _tree.upper_bound(last, &comp_abs);

			foreach(it, from, to)
			{
				size_t i = std::max(it->offset, first) - it->offset;
				size_t j = std::min(it->offset + it->key, last) - it->offset;
				res.insert(res.end(), it->value.bytes() + i, it->value.bytes() + j);
			}

			return res;
		}

	} /* detail */

} /* ng */