#include <catch2/catch.hpp>

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

TEST_CASE ( "Absorb Terminal Symbol", "[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 Ba = DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( B, a ) ) );

		grammar::CFG < > grammar ( A );

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

		grammar.addRule ( A, { B, a, C } );
		grammar.addRule ( B, { } );
		grammar.addRule ( B, { a, a, C } );
		grammar.addRule ( C, { c } );
		grammar.addRule ( C, { b, C } );

		grammar::CFG < > res = grammar;
		grammar::parsing::AbsorbTerminalSymbol::absorbTerminalSymbol ( res, a, { B } );

		grammar::CFG < > comp ( A );

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

		comp.addRule ( A, { Ba, C } );
		comp.addRule ( B, { } );
		comp.addRule ( B, { a, a, C } );
		comp.addRule ( Ba, { a } );
		comp.addRule ( Ba, { a, a, C, a } );
		comp.addRule ( C, { c } );
		comp.addRule ( C, { b, C } );

		// INFO ( alib::StringDataFactory::toString ( grammar::Grammar ( res ) ) << std::endl << alib::StringDataFactory::toString ( grammar::Grammar ( comp ) ) );

		CHECK ( res == comp );
	}

	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 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 } );
		grammar.setNonterminalAlphabet ( { A, B, C, X } );
		grammar.setInitialSymbol ( A );

		grammar.addRule ( A, { X, a, C } );
		grammar.addRule ( X, { c, B } );
		grammar.addRule ( B, { } );
		grammar.addRule ( B, { a, a, C } );
		grammar.addRule ( C, { c } );
		grammar.addRule ( C, { b, C } );

		grammar::CFG < > res = grammar;
		grammar::parsing::AbsorbTerminalSymbol::absorbTerminalSymbol ( res, a, { B, X } );

		grammar::CFG < > comp ( A );

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

		comp.addRule ( A, { Xa, C } );
		comp.addRule ( X, { c, B } );
		comp.addRule ( Xa, { c, Ba } );
		comp.addRule ( B, { } );
		comp.addRule ( B, { a, a, C } );
		comp.addRule ( Ba, { a } );
		comp.addRule ( Ba, { a, a, C, a } );
		comp.addRule ( C, { c } );
		comp.addRule ( C, { b, C } );

		// INFO ( "res " << alib::StringDataFactory::toString ( grammar::Grammar ( res ) ) << std::endl << "comp " << alib::StringDataFactory::toString ( grammar::Grammar ( comp ) ) );

		CHECK ( res == comp );
	}

	SECTION ( "Test 3" ) {
		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 Ba = DefaultSymbolType ( object::AnyObject < ext::pair < DefaultSymbolType, DefaultSymbolType > > ( ext::make_pair ( B, a ) ) );

		grammar::CFG < > grammar ( A );

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

		grammar.addRule ( A, { B, a, C } );
		grammar.addRule ( B, { } );
		grammar.addRule ( B, { a, a, B } );
		grammar.addRule ( C, { c } );
		grammar.addRule ( C, { b, C } );

		grammar::CFG < > res = grammar;
		grammar::parsing::AbsorbTerminalSymbol::absorbTerminalSymbol ( res, a, { B } );

		grammar::CFG < > comp ( A );

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

		comp.addRule ( A, { Ba, C } );
		comp.addRule ( B, { } );
		comp.addRule ( B, { a, a, B } );
		comp.addRule ( Ba, { a } );
		comp.addRule ( Ba, { a, a, Ba } );
		comp.addRule ( C, { c } );
		comp.addRule ( C, { b, C } );

		// INFO ( alib::StringDataFactory::toString ( grammar::Grammar ( res ) ) << std::endl << alib::StringDataFactory::toString ( grammar::Grammar ( comp ) ) );

		CHECK ( res == comp );
	}
}
