#include <catch2/catch.hpp>

#include <alib/list>

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

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

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

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

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

#include "factory/XmlDataFactory.hpp"
#include "factory/StringDataFactory.hpp"

#include "primitive/string/Character.h"

TEST_CASE ( "Grammar String Parser", "[unit][str][grammar]" ) {
	{
		std::string input1 = 	"CNF (\n"
					"{A, B, R, S},\n"
					"{a, b},\n"
					"{ A -> A A | a,\n"
					"  B -> B B | b,\n"
					"  R -> B R | R A,\n"
					"  S -> | B R | R A},\n"
					"S)\n";
		grammar::CNF < > grammar1 = factory::StringDataFactory::fromString (input1);
		std::string input2 = 	"CNF (\n"
					"{A, B, R, S},\n"
					"{a, b},\n"
					"{ A -> A A | a,\n"
					"  B -> B B | b,\n"
					"  R -> B R | R A,\n"
					"  S -> #E | B R | R A},\n"
					"S)\n";
		grammar::CNF < > grammar2 = factory::StringDataFactory::fromString (input2);

		std::string output1 = factory::StringDataFactory::toString(grammar1);

		CAPTURE ( input1, output1 );
		CHECK( input1 == output1 );

		grammar::CNF < > grammar3 = factory::StringDataFactory::fromString (output1);

		CHECK( grammar1 == grammar3 );
		CHECK( grammar1 == grammar2 );
	}
	{
		std::string input = 	"RIGHT_RG (\n"
					"{A, B, S},\n"
					"{a, b},\n"
					"{ A -> a | a A,\n"
					"  B -> b | b B,\n"
					"  S -> | a A | b A | b B},\n"
					"S)\n";
		grammar::RightRG < > grammar = factory::StringDataFactory::fromString (input);

		std::string output = factory::StringDataFactory::toString(grammar);

		CAPTURE ( input, output );
		CHECK( input == output );

		grammar::RightRG < > grammar2 = factory::StringDataFactory::fromString (output);

		CHECK( grammar == grammar2 );
	}
	{
		std::string input = 	"NON_CONTRACTING_GRAMMAR (\n"
					"{A, B, S},\n"
					"{a, b},\n"
					"{ A A -> B B | a A,\n"
					"  B -> b | b B,\n"
					"  S -> A S | B B | S A},\n"
					"S)\n";
		grammar::NonContractingGrammar < > grammar = factory::StringDataFactory::fromString (input);

		std::string output = factory::StringDataFactory::toString(grammar);

		CAPTURE ( input, output );

		grammar::NonContractingGrammar < > grammar2 = factory::StringDataFactory::fromString (output);

		CAPTURE ( input, output );
		CHECK( grammar == grammar2 );
	}
	{
		std::string input = 	"CSG (\n"
					"{A, B, S},\n"
					"{a, b},\n"
					"{ | B | -> b | b B,\n"
					"  | S | -> A S | B B | S A,\n"
					"  A | A | -> B B | a A},\n"
					"S)\n";
		grammar::CSG < > grammar = factory::StringDataFactory::fromString (input);

		std::string output = factory::StringDataFactory::toString(grammar);

		CAPTURE ( input, output );
		CHECK( input == output );

		grammar::CSG < > grammar2 = factory::StringDataFactory::fromString (output);

		CHECK( grammar == grammar2 );
	}
	{
		grammar::RightRG < char, char > grammar ( 'S' );
		grammar.setTerminalAlphabet ( ext::set < char > { 'a', 'b' } );
		grammar.setGeneratesEpsilon ( true );
		grammar.addRule ( 'S', ext::make_pair ( 'a', 'S' ) );
		grammar.addRule ( 'S', 'b' );

		std::string output = factory::StringDataFactory::toString(grammar);
		CAPTURE ( output );

		std::string ref = "RIGHT_RG (\n"
				  "{S, T},\n"
				  "{a, b},\n"
				  "{ S -> a S | b,\n"
				  "  T -> | a S | b},\n"
				  "T)\n";

		CHECK ( output == ref );
	}
}

