#ifndef PERSISTENT_DATA_H
#define PERSISTENT_DATA_H

#include "persistent-data/run.h"

#include <algorithm>
#include <set>

//----------------------------------------------------------------

namespace base {
	template <typename T>
	class run_set {
	public:
		void clear() {
			runs_.clear();
		}

		void add(T const &b) {
			add(run<T>(b, b + 1));
		}

		void add(T const &b, T const &e) {
			add(run<T>(b, e));
		}

		void add(run<T> const &r_) {
			run<T> r(r_);

			if (runs_.size()) {
				// Skip all blocks that end before r
				const_iterator it = runs_.lower_bound(r);
				if (it != runs_.begin())
					--it;

				while (it != runs_.end() && it->end_ < r.begin_)
					++it;

				// work out which runs overlap
				if (it != runs_.end()) {
					r.begin_ = min_maybe(it->begin_, r.begin_);
					const_iterator first = it;
					while (it != runs_.end() && it->begin_ <= r.end_) {
						r.end_ = max_maybe(it->end_, r.end_);
						++it;
					}

					// remove overlapping runs
					runs_.erase(first, it);
				}
			}

			runs_.insert(r);
		}

		void merge(run_set<T> const &rhs) {
			for (const_iterator it = rhs.begin(); it != rhs.end(); ++it)
				add(*it);
		}

		bool member(T const &v) const {
			if (!runs_.size())
				return false;

			typename rset::const_iterator it = runs_.lower_bound(run<T>(v));

			if (it->begin_ == v)
				return true;

			it--;

			if (it != runs_.end())
				return it->contains(v);

			return false;
		}

		struct compare_begin {
			bool operator ()(run<T> const &r1, run<T> const &r2) const {
				return r1.begin_ < r2.begin_;
			}
		};

		typedef std::set<run<T>, compare_begin> rset;
		typedef typename rset::const_iterator const_iterator;

		const_iterator begin() const {
			return runs_.begin();
		}

		const_iterator end() const {
			return runs_.end();
		}

		void negate() {
			rset replacement;

			if (runs_.begin() == runs_.end())
				replacement.insert(run<T>());
			else {
				typename rset::const_iterator b = runs_.begin();

				// Some versions of gcc give a spurious
				// warning here.  So we initialize it to
				// get round it.
				maybe last(0);
				last = b->end_;

				if (b->begin_)
					replacement.insert(run<T>(maybe(), *(b->begin_)));

				++b;
				while (b != runs_.end()) {
					replacement.insert(run<T>(last, b->begin_));
					last = b->end_;
					++b;
				}

				if (last)
					replacement.insert(run<T>(last, maybe()));

			}

			runs_ = replacement;
		}

	private:
		typedef typename run<T>::maybe maybe;

		static maybe min_maybe(maybe const &m1, maybe const &m2) {
			if (!m1 || !m2)
				return maybe();

			return maybe(std::min<T>(*m1, *m2));
		}

		static maybe max_maybe(maybe const &m1, maybe const &m2) {
			if (!m1 || !m2)
				return maybe();

			return maybe(std::max<T>(*m1, *m2));
		}

		rset runs_;
	};
}

//----------------------------------------------------------------

#endif
