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

#include <alib/tree>
#include <alib/utility>

namespace {
	class RegExpElement : public ext::BaseNode < RegExpElement > {
	public:
		virtual RegExpElement * clone ( ) const & = 0;
		virtual RegExpElement * clone ( ) && = 0;
	};

	class RegExpAlternation : public ext::VararyNode < RegExpElement > {
	public:
		RegExpAlternation ( RegExpElement && left, RegExpElement && right ) : VararyNode < RegExpElement > ( ) {
			pushBackChild ( std::move ( left ) );
			pushBackChild ( std::move ( right ) );
		}

		RegExpElement * clone ( ) const & override {
			return new RegExpAlternation ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpAlternation ( std::move ( * this ) );
		}
	};

	class RegExpConcatenation : public ext::BinaryNode < RegExpElement > {
	public:
		RegExpConcatenation ( RegExpElement && left, RegExpElement && right ) : BinaryNode < RegExpElement > ( std::move ( left ), std::move ( right ) ) {
		}

		RegExpElement * clone ( ) const & override {
			return new RegExpConcatenation ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpConcatenation ( std::move ( * this ) );
		}
	};

	class RegExpIteration : public ext::UnaryNode < RegExpElement > {
	public:
		RegExpIteration ( RegExpElement && element ) : UnaryNode < RegExpElement > ( std::move ( element ) ) {
		}

		RegExpIteration ( const RegExpElement & element ) : UnaryNode < RegExpElement > ( ext::move_copy ( element ) ) {
		}

		RegExpElement * clone ( ) const & override {
			return new RegExpIteration ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpIteration ( std::move ( * this ) );
		}
	};

	class RegExpSymbol : public ext::NullaryNode < RegExpElement > {
		char m_symbol;

	public:
		RegExpSymbol ( char symbol ) : m_symbol ( symbol ) {
		}

		RegExpElement * clone ( ) const & override {
			return new RegExpSymbol ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpSymbol ( std::move ( * this ) );
		}

		char getSymbol ( ) const {
			return m_symbol;
		}
	};

	class RegExpEpsilon : public ext::NullaryNode < RegExpElement > {
	public:
		RegExpElement * clone ( ) const & override {
			return new RegExpEpsilon ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpEpsilon ( std::move ( * this ) );
		}
	};

	class RegExpEmpty : public ext::NullaryNode < RegExpElement > {
	public:
		RegExpElement * clone ( ) const & override {
			return new RegExpEmpty ( * this );
		}

		RegExpElement * clone ( ) && override {
			return new RegExpEmpty ( std::move ( * this ) );
		}
	};

/*	class UnrankedTreeNode : public ext::VararyNode < UnrankedTreeNode, ext::BaseNode < UnrankedTreeNode > > {
		char m_symbol;

		public:
		UnrankedTreeNode ( char symbol, ext::ptr_vector < UnrankedTreeNode > c ) : VararyNode < UnrankedTreeNode, ext::BaseNode < UnrankedTreeNode > > ( std::move ( c ) ), m_symbol ( symbol ) {
		}

		char getSymbol ( ) const {
			return m_symbol;
		}
	};

	class UnrankedTree {
		UnrankedTreeNode root;
	};

	class RankedTreeNode : public ext::FixedaryNode < RankedTreeNode, ext::BaseNode < RankedTreeNode > > {
		char m_symbol;
		unsigned m_arity;

		public:
		RankedTreeNode ( char symbol, unsigned arity, ext::ptr_vector < RankedTreeNode > c ) : FixedaryNode < RankedTreeNode, ext::BaseNode < RankedTreeNode > > ( std::move ( c ) ), m_symbol ( symbol ), m_arity ( arity ) {
			if ( getChildren ( ).size ( ) != m_arity )
				throw "Arity != size";
		}

		char getSymbol ( ) const {
			return m_symbol;
		}
	};

	class RankedTree {
		RankedTreeNode root;
	};*/

}

TEST_CASE ( "TreeBase", "[unit][std][container]" ) {
	SECTION ( "RegExps" ) {
		RegExpIteration iter = RegExpEmpty ( );

		CHECK ( iter.getChild ( ).getParent ( ) == & iter );

		RegExpIteration iter2 = iter;
		RegExpIteration iter3 = iter2;

		CHECK ( iter3.getChild ( ).getParent ( ) == & iter3 );

		iter3 = iter;
		RegExpIteration iter4 = RegExpEpsilon ( );
		iter3 = iter4;

		CHECK ( iter3.getChild ( ).getParent ( ) == & iter3 );

		RegExpSymbol s ( 'x' );
		const RegExpSymbol & sr = s;
		sr.getChildren ( );

		RegExpAlternation alter ( std::move ( iter ), std::move ( iter2 ) );
		for ( const RegExpElement & element : alter.getChildren ( ) ) {
			CHECK ( element.getParent ( ) == & alter );
		}
	}

/*	SECTION ( "RankedTree" ) {
		RankedTreeNode node ( 'a', 1, { RankedTreeNode ( 'b', 1, { RankedTreeNode ( 'c', 0, { } ) } ) } );
		const RankedTreeNode & cnode = node;

		for ( const RankedTreeNode & child : cnode.getChildren ( ) )
			CHECK ( child.getParent ( ) == & cnode );
	}

	SECTION ( "UnrankedTree" ) {
	}*/
}
