#include <catch2/catch.hpp>

#include <alphabet/BottomOfTheStackSymbol.h>
#include <automaton/TA/DFTA.h>
#include <automaton/TA/NFTA.h>

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

#include <automaton/convert/ToPostfixPushdownAutomaton.h>

#include <factory/XmlDataFactory.hpp>

#include <label/InitialStateLabel.h>
#include <label/FinalStateLabel.h>

TEST_CASE ( "FTAtoPDA", "[unit][algo][automaton][convert]" ) {
	SECTION ( "Test DFTA to DPDA" ) {
		automaton::DFTA < > automaton;

		const common::ranked_symbol < > a (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > b (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > c (DefaultSymbolType('c'), 0);
		const ext::set<common::ranked_symbol < > > alphabet {a, b, c};
		automaton.setInputAlphabet(alphabet);

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

		ext::vector<DefaultStateType> a1States = {DefaultStateType(1), DefaultStateType(3)};
		automaton.addTransition(a, a1States, DefaultStateType(1));
		ext::vector<DefaultStateType> a2States = {DefaultStateType(3), DefaultStateType(3)};
		automaton.addTransition(a, a2States, DefaultStateType(2));
		ext::vector<DefaultStateType> bStates = {DefaultStateType(2)};
		automaton.addTransition(b, bStates, DefaultStateType(1));
		ext::vector<DefaultStateType> cStates;
		automaton.addTransition(c, cStates, DefaultStateType(3));

		automaton.addFinalState(DefaultStateType(3));

		automaton::DPDA < ext::variant < common::ranked_symbol < object::Object >, alphabet::EndSymbol >, ext::variant < object::Object, alphabet::BottomOfTheStackSymbol >, char > res = automaton::convert::ToPostfixPushdownAutomaton::convert(automaton);
		CHECK(res.getStates().size() == 2);
		CHECK(res.getTransitions().size() == 5);
		CHECK(res.getFinalStates().size() == 1);
	}

	SECTION ( "Test NFTA to NPDA" ) {
		automaton::NFTA < > automaton;

		const common::ranked_symbol < > a (DefaultSymbolType('a'), 2);
		const common::ranked_symbol < > b (DefaultSymbolType('b'), 1);
		const common::ranked_symbol < > c (DefaultSymbolType('c'), 0);
		const ext::set<common::ranked_symbol < > > alphabet {a, b, c};
		automaton.setInputAlphabet(alphabet);

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

		ext::vector<DefaultStateType> a1States = {DefaultStateType(1), DefaultStateType(3)};
		automaton.addTransition(a, a1States, DefaultStateType(1));
		automaton.addTransition(a, a1States, DefaultStateType(3));
		ext::vector<DefaultStateType> a2States = {DefaultStateType(3), DefaultStateType(3)};
		automaton.addTransition(a, a2States, DefaultStateType(2));
		ext::vector<DefaultStateType> bStates = {DefaultStateType(2)};
		automaton.addTransition(b, bStates, DefaultStateType(1));
		ext::vector<DefaultStateType> cStates;
		automaton.addTransition(c, cStates, DefaultStateType(3));

		automaton.addFinalState(DefaultStateType(3));

		automaton::NPDA < ext::variant < common::ranked_symbol < object::Object >, alphabet::EndSymbol >, ext::variant < object::Object, alphabet::BottomOfTheStackSymbol >, char > res = automaton::convert::ToPostfixPushdownAutomaton::convert ( automaton );

		CHECK(res.getStates().size() == 2);
		CHECK(res.getTransitions().size() == 6);
		CHECK(res.getFinalStates().size() == 1);
	}
}
