#include <catch2/catch.hpp>

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

TEST_CASE ( "Extract Right Context", "[unit][grammar]" ) {
	SECTION ( "Test 1" ) {
		DefaultSymbolType S = DefaultSymbolType ( "S" );
		DefaultSymbolType A = DefaultSymbolType ( "A" );
		DefaultSymbolType C = DefaultSymbolType ( "C" );

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

		grammar::CFG < > grammar ( S );

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

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

		grammar::CFG < > res = grammar;
		grammar::parsing::ExtractRightContext::extractRightContext ( res, a, { A } );

		grammar::CFG < > comp ( S );

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

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

		INFO ( "Res: " << res << ", " << comp );
		CHECK ( res == comp );
	}

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

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

		grammar::CFG < > grammar ( S );

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

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

		grammar::CFG < > res = grammar;
		grammar::parsing::ExtractRightContext::extractRightContext ( res, a, { A, X } );

		grammar::CFG < > comp ( S );

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

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

		INFO ( "Res: " << res << ", " << comp );
		CHECK ( res == comp );
	}
}
