#include <catch2/catch.hpp>
#include <alib/linear_set>
#include <alib/algorithm>

namespace {
	class Moveable {
		int& m_moves;
		int& m_copies;

		public:
		Moveable(int& moves, int& copies) : m_moves(moves), m_copies(copies) {
			m_moves = 0;
			m_copies = 0;
		}

		Moveable(const Moveable& src) : m_moves(src.m_moves), m_copies(src.m_copies) {
			m_copies++;
		}

		Moveable(Moveable&& src) : m_moves(src.m_moves), m_copies(src.m_copies) {
			m_moves++;
		}

		Moveable & operator = ( Moveable && other ) {
			std::swap ( m_moves, other.m_moves );
			std::swap ( m_copies, other.m_copies );
			return *this;
		}

		bool operator<(const Moveable&) const {
			return false;
		}
	};
}
TEST_CASE ( "LinearSet", "[unit][std][container]" ) {
	SECTION ( "Test1" ) {
		ext::vector < int > data = { 1, 4, 3 };
		ext::linear_set < int > test_set_1;
		ext::linear_set < int > test_set_2 ( data.begin ( ), data.end ( ) );

		CHECK ( test_set_1.size ( ) == 0 );
		CHECK ( test_set_1.empty ( ) );
		CHECK ( test_set_2.size ( ) == 3 );

		test_set_1.insert ( 1 );
		CHECK ( test_set_1.size ( ) == 1 );
		CHECK ( ! test_set_1.empty ( ) );
		for ( int elem : test_set_1 ) {
			CHECK ( elem == 1 );
		}
		test_set_1.clear ( );
		CHECK ( test_set_1.empty ( ) );
		ext::linear_set < int >::iterator iter = test_set_1.insert ( data [0] ).first;
		CHECK ( iter == test_set_1.begin ( ) );

		iter = test_set_1.insert ( data [1] ).first;
		CHECK ( iter == test_set_1.begin ( ) + 1 );

		iter = test_set_1.insert ( data [2] ).first;
		CHECK ( iter == test_set_1.begin ( ) + 1 );

		CHECK ( test_set_1 == test_set_2 );
		CHECK ( ! test_set_1.insert ( data [0] ).second );
		iter = test_set_1.insert ( data [0] ).first;
		CHECK ( iter == test_set_1.begin ( ) );

		test_set_1.insert ( { 2, 3 } );
		CHECK ( test_set_1.size ( ) == 4 );

		ext::vector < int > ref = { 1, 2, 3, 4 };
		ext::vector < int > copy ( test_set_1.begin ( ), test_set_1.end ( ) );
		CHECK ( ref == copy );

		ref = { 4, 3, 2, 1 };
		copy = ext::vector < int > ( test_set_1.rbegin ( ), test_set_1.rend ( ) );
		CHECK ( ref == copy );
	}

	SECTION ( "Test2" ) {
		ext::linear_set<int> first = {1};
		ext::linear_set<int> second = {1, 2, 3};

		ext::linear_set<int> firstMinusSecond;
		ext::linear_set<int> secondMinusFirst;

		std::set_difference (first.begin(), first.end(), second.begin(), second.end(), std::inserter(firstMinusSecond, firstMinusSecond.end()));
		std::set_difference (second.begin(), second.end(), first.begin(), first.end(), std::inserter(secondMinusFirst, secondMinusFirst.end()));

		CHECK(firstMinusSecond.size() == (size_t) 0u);
		CHECK(secondMinusFirst.size() == (size_t) 2u);
	}

	SECTION ( "Test3" ) {
		int moves;
		int copies;

		ext::linear_set<Moveable> set;
		set.insert ( Moveable(moves, copies) );
		ext::linear_set<Moveable> set2;

		for(Moveable moveable : ext::make_mover ( set ) ) {
			set2.insert(std::move(moveable));
		}

		CHECK(copies == 0);
	}
}
