#include <catch2/catch.hpp>
#include <compare>

#include <alib/vector>
#include <alib/deque>
#include <alib/tuple>
#include <alib/forward_tree>

namespace {
	struct RankedArityChecker {
		bool operator ()( char symbol, unsigned wantedRank ) const {
			switch ( symbol ) {
				case 'a':
					return wantedRank == 2;

				case 'b':
					return wantedRank == 1;

				default:
					return wantedRank == 0;
			}
		}

	};

	static void print_tree ( const ext::forward_tree < char > & tree, std::string indent ) {
		std::cout << indent << tree.getData ( ) << std::endl;
		for ( const ext::forward_tree < char > & child : tree.getChildren ( ) ) {
			print_tree ( child, indent + " " );
		}
	}
}
TEST_CASE ( "Forward Tree", "[unit][std][container]" ) {
	SECTION ( "Tree Std Structure" ) {
		ext::forward_tree < char > t ( 'a', ext::forward_tree < char > ( 'a' ), ext::forward_tree < char > ( 'b', ext::forward_tree < char > ( 'a' ), ext::forward_tree < char > ( 'b' ) ) );
		ext::forward_tree < char > t2 ( 'c' );

		t2.insert ( t2.begin ( ), { 'c', ext::forward_tree < char > ( 'b' ) } );

		std::swap ( t2.getChildren ( ).front (), t.getChildren().front ( ) );
		CHECK ( t2 == ext::forward_tree < char > ( 'c', ext::forward_tree < char > ( 'a' ) ) );
		CHECK ( t == ext::forward_tree < char > ( 'a', ext::forward_tree < char > ( 'c', ext::forward_tree < char > ( 'b' ) ), ext::forward_tree < char > ( 'b', ext::forward_tree < char > ( 'a' ), ext::forward_tree < char > ( 'b' ) ) ) );
		std::swap ( t2.getChildren ( ).front (), t.getChildren().front ( ) );

		t2.insert ( t2.end ( ), t );

		ext::vector < char > data = { 'd', 'e' };
		t2.insert ( t2.end ( ), data.begin ( ), data.end ( ) );

		print_tree ( t, "");

		t2.erase ( std::prev ( t2.end ( ) ) );
		t2.push_back ( 'f' );

		print_tree ( t2, "");

		CHECK( ( char ) t2 ( 1, 1 ) == 'b' );
		t2 ( 1, 1 ) = 'c';
		CHECK( ( char ) t2 ( 1, 1 ) == 'c' );

		ext::forward_tree < char >::const_prefix_iterator beg = t2.prefix_begin ( );
		beg++;
		beg++;
		beg++;
		beg++;
		beg++;
		beg++;
		beg++;
		beg--;
		beg--;
		beg--;
		beg--;
		beg--;
		beg--;
		beg--;

		CHECK ( beg == t2.prefix_begin ( ) );
		std::cout << t2 << std::endl;
		std::stringstream ss;
		ss << t2;
		CHECK ( ss.str ( ) == "[0c[1c[2b],1a[2a,2c[3a,3b]],1d,1f]]" );

		ext::vector < std::pair < unsigned, char > > expectedPrefix = { { 0, 'c' }, { 1, 'c' }, { 2, 'b' }, { 1, 'a' }, { 2, 'a' }, { 2, 'c' }, { 3, 'a' }, { 3, 'b' }, { 1, 'd' }, { 1, 'f' } };
		ext::vector < std::pair < unsigned, char > >::const_iterator ref = expectedPrefix.begin ( );

		for ( ext::forward_tree < char >::const_prefix_iterator iter = t2.prefix_begin ( ); iter != t2.prefix_end ( ); ++iter ) {
			CHECK ( iter.getLevel ( ) == ref->first );
			CHECK ( * iter == ref->second );
			++iter;
			--iter;
			++ref;
		}

		ext::vector < ext::tuple < unsigned, char, bool > > expectedStructure = { ext::make_tuple ( 0u, 'c', false ), ext::make_tuple ( 1u, 'c', false ), ext::make_tuple ( 2u, 'b', false ), ext::make_tuple ( 2u, 'b', true ), ext::make_tuple ( 1u, 'c', true ), ext::make_tuple ( 1u, 'a', false ), ext::make_tuple ( 2u, 'a', false ), ext::make_tuple ( 2u, 'a', true ), ext::make_tuple ( 2u, 'c', false ), ext::make_tuple ( 3u, 'a', false ), ext::make_tuple ( 3u, 'a', true ), ext::make_tuple ( 3u, 'b', false ), ext::make_tuple ( 3u, 'b', true ), ext::make_tuple ( 2u, 'c', true ), ext::make_tuple ( 1u, 'a', true ), ext::make_tuple ( 1u, 'd', false ), ext::make_tuple ( 1u, 'd', true ), ext::make_tuple ( 1u, 'f', false ), ext::make_tuple ( 1u, 'f', true ), ext::make_tuple ( 0u, 'c', true ) };
		ext::vector < ext::tuple < unsigned, char, bool > >::const_iterator ref2 = expectedStructure.begin ( );

		for ( ext::forward_tree < char >::const_structure_iterator iter = t2.structure_begin ( ); iter != t2.structure_end ( ); ++iter ) {
			CHECK ( iter.getLevel ( ) == std::get < 0 > ( * ref2 ) );
			CHECK ( * iter == std::get < 1 > ( * ref2 ) );
			CHECK ( iter.getVirtual ( ) == std::get < 2 > ( * ref2 ) );
			++iter;
			--iter;
			++ref2;
		}

		ext::vector < std::pair < unsigned, char > > expectedPostfix = { { 2, 'b' }, { 1, 'c' }, { 2, 'a' }, { 3, 'a' }, { 3, 'b' }, { 2, 'c' }, { 1, 'a' }, { 1, 'd' }, { 1, 'f' }, { 0, 'c' } };
		ext::vector < std::pair < unsigned, char > >::const_iterator ref3 = expectedPostfix.begin ( );

		for ( ext::forward_tree < char >::const_postfix_iterator iter = t2.postfix_begin ( ); iter != t2.postfix_end ( ); ++iter ) {
			CHECK ( iter.getLevel ( ) == ref3->first );
			CHECK ( * iter == ref3->second );
			++iter;
			--iter;
			++ref3;
		}

		ext::forward_tree < char > cmp1 ( 'a' );
		ext::forward_tree < char > cmp2 ( 'b' );
		ext::forward_tree < char > cmp3 ( 'c' );

		CHECK ( cmp1 < cmp2 );
		CHECK ( cmp2 < cmp3 );
		CHECK ( cmp2 <= cmp3 );
		CHECK ( cmp2 == cmp2 );
		CHECK ( cmp1 != cmp2 );
	}
}
