#include <catch2/catch.hpp>

#include <automaton/PDA/DPDA.h>
#include <automaton/PDA/NPDA.h>

#include <automaton/run/Accept.h>
#include <automaton/run/Translate.h>

TEST_CASE ( "PDARun", "[unit][algo][automaton][run]" ) {
	SECTION ( "Test run of DPDA vs NPDA" ) {
		automaton::DPDA < char, std::string, int > automaton ( 0, "S" );

		automaton.addState(0);
		automaton.addState(1);
		automaton.addState(2);
		automaton.addState(3);

		automaton.addInputSymbol('a');
		automaton.addInputSymbol('b');

		automaton.addPushdownStoreSymbol("S");
		automaton.addPushdownStoreSymbol("X");
		automaton.addPushdownStoreSymbol("Y");

		automaton.addTransition(0, /* eps, */ { "S" }, 1, { "X", "S" } );
		automaton.addTransition(1, 'a', { "X" }, 2, { "Y", "X" } );
		automaton.addTransition(2, 'b', { "Y", "X" }, 3, { } );

		automaton.addFinalState(3);

		CHECK ( automaton::run::Accept::accept ( automaton, string::LinearString < char > ( "ab" ) ) == true );

		CHECK ( automaton::run::Accept::accept ( automaton::NPDA < char, std::string, int > ( automaton ), string::LinearString < char > ( "ab" ) ) == true );
	}

	SECTION ( "Test run of NPDTA" ) {
		automaton::NPDTA < char, char, std::string, int > automaton ( 0, "S" );

		automaton.addState(0);
		automaton.addState(1);
		automaton.addState(2);
		automaton.addState(3);

		automaton.addInputSymbol('a');
		automaton.addInputSymbol('b');

		automaton.addOutputSymbol('a');
		automaton.addOutputSymbol('b');
		automaton.addOutputSymbol('c');

		automaton.addPushdownStoreSymbol("S");
		automaton.addPushdownStoreSymbol("X");
		automaton.addPushdownStoreSymbol("Y");

		automaton.addTransition(0, /* eps, */ { "S" }, 1, { "X", "S" }, { 'a' } );
		automaton.addTransition(1, 'a', { "X" }, 2, { "Y", "X" }, { 'b' } );
		automaton.addTransition(2, 'b', { "Y", "X" }, 3, { }, { 'c' } );

		automaton.addFinalState(3);

		std::cout << automaton::run::Translate::translate ( automaton, string::LinearString < char > ( "ab" ) ) << std::endl;
		CHECK ( automaton::run::Translate::translate ( automaton, string::LinearString < char > ( "ab" ) ).contains ( string::LinearString < char > ( "abc" ) ) );
	}

	SECTION ( "Epsilon loop test in run of PDA" ) {
		automaton::NPDA < char, char, int > automaton ( 0, '#' );

		automaton.addState(0);
		automaton.addState(1);
		automaton.addState(2);
		automaton.addState(3);
		automaton.addState(4);

		automaton.addInputSymbol('a');

		automaton.addPushdownStoreSymbol('#');
		automaton.addPushdownStoreSymbol('a');

		automaton.addTransition(0, 'a', { }, 1, { } );
		automaton.addTransition(1, /* eps, */ { '#'}, 2, { } );
		automaton.addTransition(2, /* eps, */ { }, 3, { 'a' } );
		automaton.addTransition(3, /* eps, */ { 'a' }, 2, { } );
		automaton.addTransition(3, /* eps, */ { 'a' }, 4, { } );
		automaton.addTransition(4, /* eps, */ { }, 3, { 'a' } );

		automaton.addFinalState(2);
		automaton.addFinalState(4);

		CHECK ( automaton::run::Accept::accept ( automaton, string::LinearString < char > ( "a" ) ) == true );
	}

}
