#include <catch2/catch.hpp>

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

TEST_CASE ( "LL1 Parse Table", "[unit][grammar]" ) {
	SECTION ( "Test 1" ) {
		DefaultSymbolType nE  = DefaultSymbolType ( 'E' );
		DefaultSymbolType nEp = DefaultSymbolType ( "E'" );
		DefaultSymbolType nT  = DefaultSymbolType ( 'T' );
		DefaultSymbolType nTp = DefaultSymbolType ( "T'" );
		DefaultSymbolType nF  = DefaultSymbolType ( 'F' );

		DefaultSymbolType tP = DefaultSymbolType ( '+' );
		DefaultSymbolType tS = DefaultSymbolType ( '*' );
		DefaultSymbolType tL = DefaultSymbolType ( '(' );
		DefaultSymbolType tR = DefaultSymbolType ( ')' );
		DefaultSymbolType tA = DefaultSymbolType ( 'a' );

		grammar::CFG < > grammar ( nE );
		grammar.setTerminalAlphabet ( ext::set < DefaultSymbolType > { tP, tS, tL, tR, tA } );
		grammar.setNonterminalAlphabet ( ext::set < DefaultSymbolType > { nE, nEp, nT, nTp, nF } );

		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsE1 ( { nT, nEp } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsEp1 ( { tP, nT, nEp } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsEp2 ( { } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsT1 ( { nF, nTp } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsTp1 ( { tS, nF, nTp } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsTp2 ( { } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsF1 ( { tA } );
		ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > rhsF2 ( { tL, nE, tR } );

		grammar.addRule ( nE, rhsE1 );
		grammar.addRule ( nEp, rhsEp1 );
		grammar.addRule ( nEp, rhsEp2 );
		grammar.addRule ( nT, rhsT1 );
		grammar.addRule ( nTp, rhsTp1 );
		grammar.addRule ( nTp, rhsTp2 );
		grammar.addRule ( nF, rhsF1 );
		grammar.addRule ( nF, rhsF2 );

		 // --------------------------------------------------
		ext::map < ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > parseTable;

		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tA }, nE )].insert ( rhsE1 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tL }, nE )].insert ( rhsE1 );

		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tP }, nEp )].insert ( rhsEp1 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tR }, nEp )].insert ( rhsEp2 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > ( ), nEp )].insert ( rhsEp2 );

		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tA }, nT )].insert ( rhsT1 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tL }, nT )].insert ( rhsT1 );

		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tS }, nTp )].insert ( rhsTp1 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tP }, nTp )].insert ( rhsTp2 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tR }, nTp )].insert ( rhsTp2 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > ( ), nTp )].insert ( rhsTp2 );

		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tA }, nF )].insert ( rhsF1 );
		parseTable[ext::make_pair ( ext::vector < DefaultSymbolType > { tL }, nF )].insert ( rhsF2 );

		// --------------------------------------------------

		ext::map < ext::pair < ext::vector < DefaultSymbolType >, DefaultSymbolType >, ext::set < ext::vector < ext::variant < DefaultSymbolType, DefaultSymbolType > > > > parseTableAlgo = grammar::parsing::LL1ParseTable::parseTable ( grammar );

		CHECK ( parseTable == parseTableAlgo );
	}
}
