#include <catch2/catch.hpp>

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

#include "tree/ranked/RankedTree.h"
#include "tree/xml/ranked/RankedTree.h"
#include "tree/unranked/UnrankedTree.h"
#include "tree/xml/unranked/UnrankedTree.h"
#include "tree/ranked/PrefixRankedTree.h"
#include "tree/xml/ranked/PrefixRankedTree.h"
#include "tree/ranked/PrefixRankedBarTree.h"
#include "tree/xml/ranked/PrefixRankedBarTree.h"
#include "tree/ranked/RankedPattern.h"
#include "tree/xml/ranked/RankedPattern.h"

#include "tree/TreeException.h"

#include "factory/XmlDataFactory.hpp"

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

static ext::tree < common::ranked_symbol < > > prefixToNode(const std::string & s, int & pos) {
	char c = s[pos];
	int r = s[pos+1] - '0';
	pos += 2;
	ext::vector<ext::tree < common::ranked_symbol < > >> children;
	for (int i = 0; i < r; i++) {
		children.push_back(prefixToNode(s, pos));
	}
	return ext::tree < common::ranked_symbol < > >(common::ranked_symbol < >(DefaultSymbolType(c), r), std::move(children));
}

TEST_CASE ( "Trees test", "[unit][data][tree]" ) {
	SECTION ( "Ranked Tree Parser" ) {
		/*
		   L=(a(2), b(1), c(0))

		   \-1.a(2)
		   |
		   |-2.b(1)
		   | |
		   | \-3.c(0)
		   |
		   \-4.c(0)
		   */
		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 ext::set<common::ranked_symbol < >> alphabet {a, b, c};

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

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

		CHECK( tree == tree );
		tree.getContent().nicePrint(std::cout);
		{
			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::RankedTree < > tree2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			CHECK( tree == tree2 );
			std::cout << std::endl;
			tree2.getContent().nicePrint(std::cout);
		}

		std::string s = "a2a2a2b1a2c0c0c0c0c0";
		int itmp = 0;
		ext::tree < common::ranked_symbol < > > node = prefixToNode(s, itmp);
		ext::set<common::ranked_symbol < >> al;
		for (unsigned i = 0; i < s.length(); i += 2) al.insert(common::ranked_symbol < >(DefaultSymbolType(s[i]), (int) (s[i+1] - '0')));
		tree::RankedTree < > t(al, std::move(node));
		INFO ( factory::XmlDataFactory::toString(t) );

		//TODO test it somehow
	}

	SECTION ( "Ranked Tree Compare" ) {
		/*
		   L=(a(2), b(1), c(0))

		   \-1.a(2)
		   |
		   |-2.b(1)
		   | |
		   | \-3.c(0)
		   |
		   \-4.c(0)

		   \-5.a(2)
		   |
		   |-6.c(0)
		   |
		   \-7.b(1)
		   |
		   \-8.c(0)
		   */
		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 ext::set<common::ranked_symbol < >> alphabet {a, b, c};

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

		ext::tree < common::ranked_symbol < > > node6(c, {});
		ext::tree < common::ranked_symbol < > > node8(c, {});
		ext::tree < common::ranked_symbol < > > node7(b, {std::move(node8)});
		ext::tree < common::ranked_symbol < > > node5(a, {std::move(node6), std::move(node7)});

		tree::RankedTree < > tree1(alphabet, std::move(node1));
		tree::RankedTree < > tree2(alphabet, std::move(node5));

		CHECK( tree1 != tree2 );
		CHECK( tree1 < tree2 );
		CHECK( tree2 > tree1 );
		CHECK( !(tree1 > tree2) );
	}

	SECTION ( "Ranked Tree Symbol Validity Check" ) {
		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 < > d (DefaultSymbolType('d'), 0);
		const common::ranked_symbol < > e (DefaultSymbolType('e'), 0);

		const ext::set<common::ranked_symbol < >> alphabet {a, b, c, e};

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

		CHECK_THROWS_AS(tree::RankedTree < > (alphabet, std::move(node1)), exception::CommonException);

		ext::tree < common::ranked_symbol < > > node6(e, {});
		ext::tree < common::ranked_symbol < > > node8(c, {});
		ext::tree < common::ranked_symbol < > > node7(b, {std::move(node8)});
		ext::tree < common::ranked_symbol < > > node5(a, {std::move(node6), std::move(node7)});

		tree::RankedTree < > * tree = nullptr;
		CHECK_NOTHROW(tree = new tree::RankedTree < > (alphabet, std::move(node5)));

		delete tree;
	}

	SECTION ( "Unranked Tree Parser" ) {
		/*
		   L=(a, b, c)

		   \-1.a
		   |
		   |-2.b
		   | |
		   | \-3.c
		   |
		   \-4.c
		   */
		const DefaultSymbolType a = DefaultSymbolType('a');
		const DefaultSymbolType b = DefaultSymbolType('b');
		const DefaultSymbolType c = DefaultSymbolType('c');

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

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

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

		CHECK( tree == tree );
		tree.getContent().nicePrint(std::cout);
		{
			ext::deque<sax::Token> tokens = factory::XmlDataFactory::toTokens(tree);
			std::string tmp = sax::SaxComposeInterface::composeMemory ( tokens );
			std::cout << std::endl << tmp << std::endl << std::endl;

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

			CHECK( tree == tree2 );
			std::cout << std::endl;
			tree2.getContent().nicePrint(std::cout);
		}
	}

	SECTION ( "Unranked Tree Compare" ) {
		/*
		   L=(a, b, c)

		   \-1.a
		   |
		   |-2.b
		   | |
		   | \-3.c
		   |
		   \-4.c

		   \-5.a
		   |
		   |-6.c
		   |
		   \-7.b
		   |
		   \-8.c
		   */
		const DefaultSymbolType a = DefaultSymbolType('a');
		const DefaultSymbolType b = DefaultSymbolType('b');
		const DefaultSymbolType c = DefaultSymbolType('c');

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

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

		ext::tree < DefaultSymbolType > node6(c, {});
		ext::tree < DefaultSymbolType > node8(c, {});
		ext::tree < DefaultSymbolType > node7(b, {std::move(node8)});
		ext::tree < DefaultSymbolType > node5(a, {std::move(node6), std::move(node7)});

		tree::UnrankedTree < > tree1(alphabet, std::move(node1));
		tree::UnrankedTree < > tree2(alphabet, std::move(node5));

		CHECK( tree1 != tree2 );
		CHECK( tree1 < tree2 );
		CHECK( tree2 > tree1 );
		CHECK( !(tree1 > tree2) );
	}

	SECTION ( "Unranked Tree Symbol Validity Check" ) {
		const DefaultSymbolType a = DefaultSymbolType('a');
		const DefaultSymbolType b = DefaultSymbolType('b');
		const DefaultSymbolType c = DefaultSymbolType('c');
		const DefaultSymbolType d = DefaultSymbolType('d');
		const DefaultSymbolType e = DefaultSymbolType('e');

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

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

		CHECK_THROWS_AS(tree::UnrankedTree < > (alphabet, std::move(node1)), exception::CommonException);

		ext::tree < DefaultSymbolType > node6(e, {});
		ext::tree < DefaultSymbolType > node8(c, {});
		ext::tree < DefaultSymbolType > node7(b, {std::move(node8)});
		ext::tree < DefaultSymbolType > node5(a, {std::move(node6), std::move(node7)});

		tree::UnrankedTree < > * tree = nullptr;
		CHECK_NOTHROW(tree = new tree::UnrankedTree < > (alphabet, std::move(node5)));

		delete tree;
	}

	SECTION ( "Prefix Ranked Bar Parser" ) {
		const DefaultSymbolType bar{alphabet::BarSymbol{}};

		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 ext::set<common::ranked_symbol < >> alphabet {a, b, c};

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

		tree::RankedTree < > tree(alphabet, std::move(node1));
		tree::PrefixRankedBarTree < > tree2(bar, tree);

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

			std::cout << tmp << std::endl << std::endl;

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

			CHECK( tree2 == tree3 );
			std::cout << std::endl;
		}
	}

	SECTION ( "Postfix to Prefix" ) {
		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 ext::set<common::ranked_symbol < >> alphabet {a, b, c};

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

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

		tree::PrefixRankedTree < > prefixRanked ( tree );
		tree::PostfixRankedTree < > postfixRanked ( tree );
		tree::PrefixRankedTree < > prefixRanked2 ( postfixRanked );

		CHECK ( prefixRanked == prefixRanked2 );
	}
}
