#include <catch2/catch.hpp>

#include "grammar/ContextFree/CFG.h"
#include "grammar/parsing/HandleFirstFollowConflict.h"
#include "factory/StringDataFactory.hpp"

TEST_CASE ( "Handle First Follow Conflict", "[unit][grammar]" ) {
	SECTION ( "Test 1" ) {
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

		DefaultSymbolType a = DefaultSymbolType ( 'a' );
		DefaultSymbolType b = DefaultSymbolType ( 'b' );
		DefaultSymbolType c = DefaultSymbolType ( 'c' );
		DefaultSymbolType d = DefaultSymbolType ( 'd' );

		DefaultSymbolType Ba = DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( B, a ) ) );

		grammar::CFG < > grammar ( A );

		grammar.setTerminalAlphabet ( { a, b, c, d } );
		grammar.setNonterminalAlphabet ( { A, B, C } );
		grammar.setInitialSymbol ( A );

		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, C } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, a, d } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );

		grammar::CFG < > res = grammar;
		grammar::parsing::HandleFirstFollowConflict::handleFirstFollowConflict ( res, a, B, { { a, B, C }, { } } );

		grammar::CFG < > comp ( A );

		comp.setTerminalAlphabet ( { a, b, c, d } );
		comp.setNonterminalAlphabet ( { A, B, C } );
		comp.setInitialSymbol ( A );

		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, a, C } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, b, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, a, d } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );

		CHECK ( res == comp );

		grammar::parsing::HandleFirstFollowConflict::handleFirstFollowConflict ( res, a, B, { { a, B, C }, { } } );

		grammar::CFG < > comp2 ( A );

		comp2.setTerminalAlphabet ( { a, b, c, d } );
		comp2.setNonterminalAlphabet ( { A, B, Ba, C } );
		comp2.setInitialSymbol ( A );

		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ba, C } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, b, B } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ba, d } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		comp2.addRule ( Ba, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ba, d, a } );
		comp2.addRule ( Ba, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		comp2.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp2.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );

		CHECK ( res == comp2 );
	}

	SECTION ( "Test 2" ) {
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );
		DefaultSymbolType X = DefaultSymbolType ( "X" );

		DefaultSymbolType a = DefaultSymbolType ( 'a' );
		DefaultSymbolType b = DefaultSymbolType ( 'b' );
		DefaultSymbolType c = DefaultSymbolType ( 'c' );
		DefaultSymbolType d = DefaultSymbolType ( 'd' );

		DefaultSymbolType Ba = DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( B, a ) ) );
		DefaultSymbolType Xa = DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( X, a ) ) );

		grammar::CFG < > grammar ( A );

		grammar.setTerminalAlphabet ( { a, b, c, d } );
		grammar.setNonterminalAlphabet ( { A, B, C, X } );
		grammar.setInitialSymbol ( A );

		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, X, C, a } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		grammar.addRule ( X, { c, B });
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, a, d } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		grammar.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, c } );

		grammar::CFG < > res = grammar;
		grammar::parsing::HandleFirstFollowConflict::handleFirstFollowConflict ( res, a, B, { { a, B, C }, { } } );

		grammar::CFG < > comp ( A );

		comp.setTerminalAlphabet ( { a, b, c, d } );
		comp.setNonterminalAlphabet ( { A, B, C, X } );
		comp.setInitialSymbol ( A );

		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, X, a, C, a } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, X, b, B, c, a } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		comp.addRule ( X, { c, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, B, a, d } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, c } );

		CHECK ( res == comp );

		grammar::parsing::HandleFirstFollowConflict::handleFirstFollowConflict ( res, a, B, { { a, B, C }, { } } );

		grammar::CFG < > comp2 ( A );

		comp2.setTerminalAlphabet ( { a, b, c, d } );
		comp2.setNonterminalAlphabet ( { A, B, Ba, C, X, Xa } );
		comp2.setInitialSymbol ( A );

		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Xa, C, a } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, X, b, B, c, a } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, C } );
		comp2.addRule ( X, { c, B } );
		comp2.addRule ( Xa, { c, Ba } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ba, d } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { } );
		comp2.addRule ( Ba, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ba, d, a } );
		comp2.addRule ( Ba, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		comp2.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C } );
		comp2.addRule ( C, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, c } );

		CHECK ( res == comp2 );
	}
}
