#ifndef INTEGERRANGE_H
#define INTEGERRANGE_H

#include <algorithm>

template <typename Value>
class IntegerRange {
public:
	using Val = Value;

	IntegerRange() = default;
	IntegerRange(Value start, Value length)
		: m_start(start)
		, m_length(length)
	{}

	bool canCombine(const IntegerRange &rhs) const
	{
		if (m_start == rhs.m_start) {
			return true;
		}
		if (m_start < rhs.m_start) {
			return m_start + m_length >= rhs.m_start;
		}
		else {
			return rhs.canCombine(*this);
		}
	}

	void setLength(Value length)
	{
		m_length = length;
	}

	Value length() const { return m_length; }

	void setStart(Value start)
	{
		m_start = start;
	}
	Value start() const { return m_start; }

	void setEnd(Value end)
	{
		m_length = end - m_start;
	}
	Value end() const { return m_start + m_length; }

	bool operator==(const IntegerRange &rhs) const
	{
		return m_start == rhs.m_start && m_length == rhs.m_length;
	}

	bool operator!=(const IntegerRange &rhs) const
	{
		return !operator==(rhs);
	}

	bool operator<(const IntegerRange &rhs) const
	{
		return m_start < rhs.m_start;
	}

	bool operator<=(const IntegerRange &rhs) const
	{
		return m_start < rhs.m_start || (m_start == rhs.m_start && m_length < rhs.m_length);
	}

	bool operator>(const IntegerRange &rhs) const
	{
		return m_start > rhs.m_start;
	}

	bool operator>=(const IntegerRange &rhs) const
	{
		return m_start > rhs.m_start || (m_start == rhs.m_start && m_length > rhs.m_length);
	}
private:
	Value m_start = 0;
	Value m_length = 0;
};

/// This function merges elements that overlap or touch into single elements
/// and stores the resulting elements into the destination range starting at d_first
///
/// Function assumes the input range is sorted
template <typename InputIt, typename OutputIt>
OutputIt merge_ranges(InputIt first, InputIt last, OutputIt d_first)
{
	using Elem = typename InputIt::value_type;

	InputIt i = first;
	while (first != last) {
		Elem e = *first;
		++first;
		while (first != last && e.canCombine(*first)) {
			if (first->end() > e.end())
				e.setEnd(first->end());
			++first;
		}
		d_first = e;
	}
	return d_first;
}

//template <typename Container, typename RangeType>
//void insert(Container cont, RangeType range)
//{
//	auto ipos = std::lower_bound(begin(cont), end(cont), range);

//	// Maybe it can be combined with previous
//	if (ipos != begin(cont)) {
//		auto prev = ipos;
//		--prev;
//		if (prev->canCombine(range))
//			prev->setEnd(range->end());

//	}
//	else {
//		// We need to either insert it or combine with the next
//		if (ipos != end(cont) && range.canCombine(*ipos)) {
//			// remember where end of ipos range was
//			auto e = ipos->end();
//			if (e > range.end())
//				range.setEnd(e);
//			// We cannot modify element in set so we remove one element and add a new one
//			ipos = cont.erase(ipos);
//			cont.insert(ipos, range);
//		}
//		else {
//			cont->insert(ipos, range);
//		}
//	}
//}

#endif // INTEGERRANGE_H
