#include <catch2/catch.hpp>

#include "grammar/ContextFree/CFG.h"
#include "grammar/parsing/HandleFirstFirstConflict.h"

TEST_CASE ( "Handle First First Conflict", "[unit][grammar]" ) {
	SECTION ( "Test 1" ) {
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType Ap = DefaultSymbolType ( object::AnyObject < std::string > ( "A", 1 ) );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

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

		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 } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { d } );
		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::HandleFirstFirstConflict::handleFirstFirstConflict ( res, a, A, { {a, B }, {C ,B} } );

		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 } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { d } );
		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::HandleFirstFirstConflict::handleFirstFirstConflict ( res, a, A, { {a, B }, {a, C, B} } );

		grammar::CFG < > comp2 ( A );

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

		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ap } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B, B } );
		comp2.addRule ( Ap, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { B } );
		comp2.addRule ( Ap, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { d } );
		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 Ap = DefaultSymbolType ( object::AnyObject < std::string > ( "A", 1 ) );
		DefaultSymbolType B = DefaultSymbolType ( "B" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

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

		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 } );
		grammar.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		grammar.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		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::HandleFirstFirstConflict::handleFirstFirstConflict ( res, a, A, { {a, B}, {C, B}});
		grammar::parsing::HandleFirstFirstConflict::handleFirstFirstConflict ( res, a, A, { {a, B}, {a, C, B}, {B, b, B}});

		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 } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, C, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, b, B } );
		comp.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B, b, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a } );
		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::HandleFirstFirstConflict::handleFirstFirstConflict ( res, a, A, { {a, B}, {a, C, B}, {a, b, B}});

		grammar::CFG < > comp2 ( A );

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

		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { a, Ap } );
		comp2.addRule ( A, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B, b, B } );
		comp2.addRule ( Ap, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { B } );
		comp2.addRule ( Ap, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { C, B } );
		comp2.addRule ( Ap, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { b, B } );
		comp2.addRule ( B, ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > { c, B } );
		comp2.addRule ( B, 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 );
	}
}
