/*
 * NFADeterminizer.cpp
 *
 *  Created on: 16. 1. 2014
 *	  Author: Jan Travnicek
 */

#include "common/RHDPDACommon.h"

#include <alphabet/BottomOfTheStackSymbol.h>
#include <automaton/PDA/VisiblyPushdownNPDA.h>
#include <alib/set>

namespace automaton {

namespace determinize {

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
automaton::VisiblyPushdownDPDA < InputSymbolType, ext::pair < ext::set < ext::pair < StateType, StateType > >, InputSymbolType >, ext::set < ext::pair < StateType, StateType > > > Determinize::determinize ( const automaton::VisiblyPushdownNPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & npda ) {
	using DeterministicStateType = ext::set < ext::pair < StateType, StateType > >;
	using DeterministicPushdownStoreSymbolType = ext::pair < DeterministicStateType, InputSymbolType >;

	DeterministicStateType initialLabel = createIdentity(npda.getInitialStates());
	DeterministicPushdownStoreSymbolType bottom = ext::make_pair ( DeterministicStateType { }, alphabet::BottomOfTheStackSymbol::instance < InputSymbolType > ( ) );

	automaton::VisiblyPushdownDPDA < InputSymbolType, DeterministicPushdownStoreSymbolType, DeterministicStateType > dpda(initialLabel, bottom);

	dpda.setCallInputAlphabet(npda.getCallInputAlphabet());
	dpda.setLocalInputAlphabet(npda.getLocalInputAlphabet());
	dpda.setReturnInputAlphabet(npda.getReturnInputAlphabet());

	for(;;) {
		ext::set<ext::pair<DeterministicStateType, DeterministicPushdownStoreSymbolType>> stateSymbols = existsDirtyStateSymbol(dpda, npda);
		ext::set<DeterministicStateType> states = existsDirtyState(dpda, npda);

		if(stateSymbols.empty() && states.empty()) break;

		for ( const ext::pair < DeterministicStateType, DeterministicPushdownStoreSymbolType > & stateSymbol : stateSymbols ) {
			const DeterministicStateType & state = stateSymbol.first;
			const DeterministicPushdownStoreSymbolType & pdaSymbol = stateSymbol.second;

			for ( const InputSymbolType & input : npda.getReturnInputAlphabet ( ) ) {
				if ( pdaSymbol == dpda.getBottomOfTheStackSymbol ( ) ) {
					DeterministicStateType to = retInitial ( state, input, npda );

					dpda.addState ( to );
					dpda.addReturnTransition ( state, input, pdaSymbol, std::move ( to ) );
				} else {
					DeterministicStateType to = ret ( state, pdaSymbol, input, npda );

					dpda.addState ( to );
					dpda.addReturnTransition ( state, input, pdaSymbol, std::move ( to ) );
				}
			}
		}

		for ( const DeterministicStateType & state : states ) {
			for ( const InputSymbolType & input : npda.getLocalInputAlphabet ( ) ) {
				DeterministicStateType to = local ( state, input, npda );

				dpda.addState ( to );
				dpda.addLocalTransition ( state, input, std::move ( to ) );
			}
			for ( const InputSymbolType & input : npda.getCallInputAlphabet ( ) ) {
				DeterministicStateType to = call ( state, input, npda );
				ext::pair < DeterministicStateType, InputSymbolType > pdaSymbol = ext::make_pair ( state, input );

				dpda.addState ( to );
				dpda.addPushdownStoreSymbol ( pdaSymbol );
				dpda.addCallTransition ( state, input, std::move ( to ), std::move ( pdaSymbol ) );
			}
		}
	}

	const ext::set<StateType>& finalLabels = npda.getFinalStates();
	for(const DeterministicStateType & state : dpda.getStates()) {
		const ext::set<StateType> labels = retrieveDSubSet(state);

		if(!ext::excludes(finalLabels.begin(), finalLabels.end(), labels.begin(), labels.end())) {
			dpda.addFinalState(state);
		}
	}

	return dpda;
}

} /* namespace determinize */

} /* namespace automaton */
