#include <catch2/catch.hpp>

#include "sax/SaxParseInterface.h"
#include "sax/SaxComposeInterface.h"

#include "tree/unranked/UnrankedPattern.h"
#include "tree/xml/unranked/UnrankedPattern.h"
#include "tree/ranked/RankedPattern.h"
#include "tree/xml/ranked/RankedPattern.h"
#include "tree/ranked/PrefixRankedPattern.h"
#include "tree/xml/ranked/PrefixRankedPattern.h"
#include "tree/ranked/PrefixRankedBarPattern.h"
#include "tree/xml/ranked/PrefixRankedBarPattern.h"

#include "tree/TreeException.h"

#include "factory/XmlDataFactory.hpp"

#include "common/ranked_symbol.hpp"
#include "alphabet/WildcardSymbol.h"
#include "alphabet/BarSymbol.h"
#include "alphabet/VariablesBarSymbol.h"

TEST_CASE ( "Pattern test", "[unit][data][tree]" ) {
	SECTION ( "Ranked Pattern Parser" ) {
		const common::ranked_symbol < > a (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > b (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > c (DefaultSymbolType('c'), 0);

		const common::ranked_symbol < > S (DefaultSymbolType { alphabet::WildcardSymbol {} }, 0);
		const ext::set<common::ranked_symbol < >> alphabet {a, b, c, S};

		ext::tree < common::ranked_symbol < > > node3(c, {});
		ext::tree < common::ranked_symbol < > > node4(S, {});
		ext::tree < common::ranked_symbol < > > node2(b, {std::move(node3)});
		ext::tree < common::ranked_symbol < > > node1(a, {std::move(node2), std::move(node4)});

		tree::RankedPattern < > tree(S, alphabet, std::move(node1));

		std::ostringstream oss;
		tree.getContent().nicePrint(oss);
		INFO ( "Tree: " << oss.str ( ) );
		CHECK( tree == tree );
		{
			ext::deque<sax::Token> tokens = factory::XmlDataFactory::toTokens(tree);
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			tree::RankedPattern < > tree2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			oss.str ( "" );
			oss.clear ( );
			tree2.getContent().nicePrint( oss );
			INFO ( "Tree2:" << oss.str ( ) );
			CHECK( tree == tree2 );
		}

		const DefaultSymbolType ua = DefaultSymbolType('a');
		const DefaultSymbolType ub = DefaultSymbolType('b');
		const DefaultSymbolType uc = DefaultSymbolType('c');

		const DefaultSymbolType uS { alphabet::WildcardSymbol {} };
		const ext::set<DefaultSymbolType> ualphabet {ua, ub, uc, uS};

		ext::tree < DefaultSymbolType > unode3(uc, {});
		ext::tree < DefaultSymbolType > unode4(uS, {});
		ext::tree < DefaultSymbolType > unode2(ub, {std::move(unode3)});
		ext::tree < DefaultSymbolType > unode1(ua, {std::move(unode2), std::move(unode4)});

		tree::UnrankedPattern < > tree2(uS, ualphabet, std::move(unode1));

		tree::UnrankedPattern < > tree3 ( tree );

		CHECK(tree3 == tree2);
	}

	SECTION ( "Unranked Pattern Parser" ) {
		const DefaultSymbolType a = DefaultSymbolType('a');
		const DefaultSymbolType b = DefaultSymbolType('b');
		const DefaultSymbolType c = DefaultSymbolType('c');

		const DefaultSymbolType S { alphabet::WildcardSymbol {} };
		const ext::set<DefaultSymbolType> alphabet {a, b, c, S};

		ext::tree < DefaultSymbolType > node3(c, {});
		ext::tree < DefaultSymbolType > node4(S, {});
		ext::tree < DefaultSymbolType > node2(b, {std::move(node3)});
		ext::tree < DefaultSymbolType > node1(a, {std::move(node2), std::move(node4)});

		tree::UnrankedPattern < > tree(S, alphabet, std::move(node1));

		std::ostringstream oss;
		tree.getContent().nicePrint(oss);
		INFO ( "Tree1: " << oss.str ( ) );
		CHECK( tree == tree );
		{
			ext::deque<sax::Token> tokens = factory::XmlDataFactory::toTokens(tree);
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			tree::UnrankedPattern < > tree2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			oss.str ( "" );
			oss.clear ( );
			tree2.getContent().nicePrint ( oss );
			INFO ( "Tree2: " << oss.str ( ) );
			CHECK( tree == tree2 );
		}

		const common::ranked_symbol < > ra (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > rb (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > rc (DefaultSymbolType('c'), 0);

		const common::ranked_symbol < > rS { DefaultSymbolType { alphabet::WildcardSymbol {} }, 0};
		const ext::set<common::ranked_symbol < >> ralphabet {ra, rb, rc, rS};

		ext::tree < common::ranked_symbol < > > rnode3(rc, {});
		ext::tree < common::ranked_symbol < > > rnode4(rS, {});
		ext::tree < common::ranked_symbol < > > rnode2(rb, {std::move(rnode3)});
		ext::tree < common::ranked_symbol < > > rnode1(ra, {std::move(rnode2), std::move(rnode4)});

		tree::RankedPattern < > tree2(rS, ralphabet, std::move(rnode1));

		tree::RankedPattern < > tree3 (tree);

		CHECK(tree3 == tree2);
	}

	SECTION ( "Prefix Ranked Pattern Parser" ) {
		const common::ranked_symbol < > a (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > b (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > c (DefaultSymbolType('c'), 0);

		const common::ranked_symbol < > S (DefaultSymbolType { alphabet::WildcardSymbol {} }, 0);
		const ext::set<common::ranked_symbol < >> alphabet {a, b, c, S};

		ext::tree < common::ranked_symbol < > > node3(c, {});
		ext::tree < common::ranked_symbol < > > node4(S, {});
		ext::tree < common::ranked_symbol < > > node2(b, {std::move(node3)});
		ext::tree < common::ranked_symbol < > > node1(a, {std::move(node2), std::move(node4)});

		tree::RankedPattern < > pattern(S, alphabet, std::move(node1));
		tree::PrefixRankedPattern < > pattern2(pattern);

		CHECK( pattern2 == pattern2 );
		{
			ext::deque<sax::Token> tokens = factory::XmlDataFactory::toTokens(pattern2);
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			tree::PrefixRankedPattern < > pattern3 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			CHECK( pattern2 == pattern3 );
		}
	}

	SECTION ( "Prefix Ranked Bar Pattern Parser" ) {
		const common::ranked_symbol < > a (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > b (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > c (DefaultSymbolType('c'), 0);

		const common::ranked_symbol < > S (DefaultSymbolType { alphabet::WildcardSymbol {} }, 0);
		const ext::set<common::ranked_symbol < >> alphabet {a, b, c, S};

		ext::tree < common::ranked_symbol < > > node3(c, {});
		ext::tree < common::ranked_symbol < > > node4(S, {});
		ext::tree < common::ranked_symbol < > > node2(b, {std::move(node3)});
		ext::tree < common::ranked_symbol < > > node1(a, {std::move(node2), std::move(node4)});

		tree::RankedPattern < > pattern(S, alphabet, std::move(node1));
		tree::PrefixRankedBarPattern < > pattern2(alphabet::BarSymbol::instance < DefaultSymbolType > ( ), alphabet::VariablesBarSymbol::instance < common::ranked_symbol < DefaultSymbolType > > ( ), pattern);

		CHECK( pattern2 == pattern2 );
		{
			ext::deque<sax::Token> tokens = factory::XmlDataFactory::toTokens(pattern2);
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );

			CAPTURE ( tmp );

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			tree::PrefixRankedBarPattern < > pattern3 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			CHECK( pattern2 == pattern3 );
		}
	}
}

