#include <catch2/catch.hpp>

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

#include "grammar/Unrestricted/UnrestrictedGrammar.h"
#include "grammar/xml/Unrestricted/UnrestrictedGrammar.h"
#include "grammar/Unrestricted/ContextPreservingUnrestrictedGrammar.h"
#include "grammar/xml/Unrestricted/ContextPreservingUnrestrictedGrammar.h"

#include "grammar/Regular/RightRG.h"
#include "grammar/xml/Regular/RightRG.h"
#include "grammar/Regular/RightLG.h"
#include "grammar/xml/Regular/RightLG.h"

#include "grammar/Regular/LeftRG.h"
#include "grammar/xml/Regular/LeftRG.h"
#include "grammar/Regular/LeftLG.h"
#include "grammar/xml/Regular/LeftLG.h"

#include "grammar/ContextFree/LG.h"
#include "grammar/xml/ContextFree/LG.h"
#include "grammar/ContextFree/EpsilonFreeCFG.h"
#include "grammar/xml/ContextFree/EpsilonFreeCFG.h"
#include "grammar/ContextFree/CFG.h"
#include "grammar/xml/ContextFree/CFG.h"
#include "grammar/ContextFree/CNF.h"
#include "grammar/xml/ContextFree/CNF.h"
#include "grammar/ContextFree/GNF.h"
#include "grammar/xml/ContextFree/GNF.h"

#include "grammar/ContextSensitive/CSG.h"
#include "grammar/xml/ContextSensitive/CSG.h"
#include "grammar/ContextSensitive/NonContractingGrammar.h"
#include "grammar/xml/ContextSensitive/NonContractingGrammar.h"

#include "factory/XmlDataFactory.hpp"

#include <primitive/xml/Integer.h>
#include <primitive/xml/String.h>

TEST_CASE ( "Grammar test", "[unit][data][grammar]" ) {
	SECTION ( "Unrestricted Parser" ) {
		{
			grammar::UnrestrictedGrammar < > grammar(DefaultSymbolType(1));

			grammar.addNonterminalSymbol(DefaultSymbolType(1));
			grammar.addNonterminalSymbol(DefaultSymbolType(2));
			grammar.addNonterminalSymbol(DefaultSymbolType(3));
			grammar.addTerminalSymbol(DefaultSymbolType("a"));
			grammar.addTerminalSymbol(DefaultSymbolType("b"));

			grammar.addRule({DefaultSymbolType(1)}, {DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule({DefaultSymbolType(2)}, {DefaultSymbolType("b"), DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(2), DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("b"), DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(1)}, ext::vector<DefaultSymbolType> {DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(1)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {});

			CHECK( grammar == grammar );

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

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			grammar::UnrestrictedGrammar < > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			CHECK( grammar == grammar2 );
		}
		{
			grammar::ContextPreservingUnrestrictedGrammar < > grammar(DefaultSymbolType(1));

			grammar.addNonterminalSymbol(DefaultSymbolType(1));
			grammar.addNonterminalSymbol(DefaultSymbolType(2));
			grammar.addNonterminalSymbol(DefaultSymbolType(3));
			grammar.addTerminalSymbol(DefaultSymbolType("a"));
			grammar.addTerminalSymbol(DefaultSymbolType("b"));

			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(2), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType("b"), DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(2), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {});

			CHECK( grammar == grammar );

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

			ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
			grammar::ContextPreservingUnrestrictedGrammar < > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

			CHECK( grammar == grammar2 );
		}
	}

	SECTION ( "Regular Parser" ) {
		{
			grammar::RightRG < signed char, int > grammar ( 1 );

			grammar.addTerminalSymbol ( 2 );
			CHECK_THROWS_AS ( grammar.addNonterminalSymbol ( 2 ), exception::CommonException );
		}
		{
			grammar::RightRG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::make_pair ( std::string ( "a" ), 2 ) );
			grammar.addRule ( 2, ext::make_pair ( std::string ( "b" ), 3 ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::RightRG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}

		{
			grammar::RightLG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::make_pair ( ext::vector < std::string > { "a" }, 2 ) );
			grammar.addRule ( 2, ext::make_pair ( ext::vector < std::string > { "b" }, 3 ) );
			grammar.addRule ( 1, ext::make_pair ( ext::vector < std::string > {}, 3 ) );
			grammar.addRule ( 1, ext::make_pair ( ext::vector < std::string > { "a", "a", "a" }, 2 ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::RightLG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}

		{
			grammar::LeftRG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::make_pair ( 2, std::string ( "a" ) ) );
			grammar.addRule ( 2, ext::make_pair ( 3, std::string ( "b" ) ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::LeftRG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}

		{
			grammar::LeftLG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::make_pair ( 2, ext::vector < std::string > { "a" } ) );
			grammar.addRule ( 2, ext::make_pair ( 3, ext::vector < std::string > { "b" } ) );
			grammar.addRule ( 1, ext::make_pair ( 3, ext::vector < std::string > {} ) );
			grammar.addRule ( 1, ext::make_pair ( 2, ext::vector < std::string > { "a", "a", "a" } ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::LeftLG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
	}

	SECTION ( "CFG Parser" ) {
		{
			grammar::LG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::make_tuple ( ext::vector < std::string > { "a" }, 2, ext::vector < std::string > { } ) );
			grammar.addRule ( 2, ext::make_tuple ( ext::vector < std::string > { "b" }, 3, ext::vector < std::string > { } ) );
			grammar.addRule ( 1, ext::make_tuple ( ext::vector < std::string > { }, 3, ext::vector < std::string > { } ) );
			grammar.addRule ( 1, ext::make_tuple ( ext::vector < std::string > { "a", "a", "a"}, 2, ext::vector < std::string > { } ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::LG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
		{
			grammar::EpsilonFreeCFG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );
			grammar.setGeneratesEpsilon ( true );

			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { "a" }, { 2 } } );
			grammar.addRule ( 2, ext::vector < ext::variant < std::string, int > > { { "b" }, { 3 } } );
			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { 3 } } );
			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { "a" }, { "a" }, { "a" }, { 2 } } );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::EpsilonFreeCFG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
		{
			grammar::CFG < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );

			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { "a" }, { 2 } } );
			grammar.addRule ( 2, ext::vector < ext::variant < std::string, int > > { { "b" }, { 3 } } );
			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { 3 } } );
			grammar.addRule ( 1, ext::vector < ext::variant < std::string, int > > { { "a" }, { "a" }, { "a" }, { 2 } } );
			grammar.addRule ( 2, ext::vector < ext::variant < std::string, int > > { } );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::CFG < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
		{
			grammar::CNF < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );
			grammar.setGeneratesEpsilon ( true );

			grammar.addRule ( 1, ext::make_pair ( 3, 2 ) );
			grammar.addRule ( 2, ext::make_pair ( 3, 3 ) );
			grammar.addRule ( 3, std::string ( "a" ) );
			grammar.addRule ( 1, std::string ( "a" ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::CNF < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
		{
			grammar::GNF < std::string, int > grammar ( 1 );

			grammar.addNonterminalSymbol ( 1 );
			grammar.addNonterminalSymbol ( 2 );
			grammar.addNonterminalSymbol ( 3 );
			grammar.addTerminalSymbol ( "a" );
			grammar.addTerminalSymbol ( "b" );
			grammar.setGeneratesEpsilon ( true );

			grammar.addRule ( 1, ext::make_pair ( "a", ext::vector < int > { 2 } ) );
			grammar.addRule ( 2, ext::make_pair ( "b", ext::vector < int > { 3 } ) );
			grammar.addRule ( 3, ext::make_pair ( "a", ext::vector < int > { } ) );
			grammar.addRule ( 1, ext::make_pair ( "a", ext::vector < int > { } ) );

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::GNF < std::string, int > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
	}

	SECTION ( "CSG Parser" ) {
		{
			grammar::CSG < > grammar(DefaultSymbolType(1));

			grammar.addNonterminalSymbol(DefaultSymbolType(1));
			grammar.addNonterminalSymbol(DefaultSymbolType(2));
			grammar.addNonterminalSymbol(DefaultSymbolType(3));
			grammar.addTerminalSymbol(DefaultSymbolType("a"));
			grammar.addTerminalSymbol(DefaultSymbolType("b"));

			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(2), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType("b"), DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {}, DefaultSymbolType(1), ext::vector<DefaultSymbolType> {}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType(2)});

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::CSG < > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
		{
			grammar::NonContractingGrammar < > grammar(DefaultSymbolType(1));

			grammar.addNonterminalSymbol(DefaultSymbolType(1));
			grammar.addNonterminalSymbol(DefaultSymbolType(2));
			grammar.addNonterminalSymbol(DefaultSymbolType(3));
			grammar.addTerminalSymbol(DefaultSymbolType("a"));
			grammar.addTerminalSymbol(DefaultSymbolType("b"));

			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(2), DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType(2)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(2)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("b"), DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(1)}, ext::vector<DefaultSymbolType> {DefaultSymbolType(3)});
			grammar.addRule(ext::vector<DefaultSymbolType> {DefaultSymbolType(1)}, ext::vector<DefaultSymbolType> {DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType("a"), DefaultSymbolType(2)});

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

				ext::deque<sax::Token> tokens2 = sax::SaxParseInterface::parseMemory ( tmp );
				grammar::NonContractingGrammar < > grammar2 = factory::XmlDataFactory::fromTokens (std::move(tokens2));

				CHECK( grammar == grammar2 );
			}
		}
	}
}
