/*
 * SingleInitialStateEpsilonTransition.h
 *
 * This file is part of Algorithms library toolkit.
 * Copyright (C) 2017 Jan Travnicek (jan.travnicek@fit.cvut.cz)

 * Algorithms library toolkit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.

 * Algorithms library toolkit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with Algorithms library toolkit.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  Created on: 6. 6. 2018
 *	  Author: Jan Travnicek
 */

#pragma once

#include <automaton/FSM/DFA.h>
#include <automaton/FSM/NFA.h>
#include <automaton/FSM/MultiInitialStateNFA.h>
#include <automaton/FSM/MultiInitialStateEpsilonNFA.h>
#include <automaton/FSM/ExtendedNFA.h>
#include <automaton/FSM/EpsilonNFA.h>
#include <automaton/FSM/CompactNFA.h>

#include <label/InitialStateLabel.h>
#include <common/createUnique.hpp>

namespace automaton {

namespace simplify {

/**
 * Algorithm for the conversion of multi-initial state finite automata to single-initial state finite automata using epsilon transitions.
 *
 * @sa automaton::simplify::SingleInitialState
 */
class SingleInitialStateEpsilonTransition {
public:
	/**
	 * Converts multi-initial state automaton to a single-initial state automaton with the use of epsilon transitions.
	 * @tparam T type of the converted automaton.
	 *
	 * @param automaton automaton to convert
	 *
	 * @return an epsilon automaton equivalent to @p with only one initial state
	 */
	template < class T >
	static ext::require < isMultiInitialStateNFA < T > || isMultiInitialStateEpsilonNFA < T >, automaton::EpsilonNFA < typename T::StateType, typename T::SymbolType > > convert ( const T & fsm );

	/**
	 * @overload
	 */
	template < class T >
	static ext::require < isDFA < T > || isNFA < T > || isEpsilonNFA < T > || isExtendedNFA < T > || isCompactNFA < T >, T > convert ( const T & fsm );
};

template < class T >
ext::require < isMultiInitialStateNFA < T > || isMultiInitialStateEpsilonNFA < T >, automaton::EpsilonNFA < typename T::StateType, typename T::SymbolType > > SingleInitialStateEpsilonTransition::convert ( const T & fsm ) {
	using StateType = typename T::StateType;
	using SymbolType = typename T::SymbolType;

	// step 1, 3
	StateType q0 = common::createUnique ( label::InitialStateLabel::instance < StateType > ( ), fsm.getStates ( ) );

	automaton::EpsilonNFA < SymbolType, StateType > res ( q0 );
	res.setInputAlphabet ( fsm.getInputAlphabet ( ) );

	for( const StateType & q : fsm.getStates ( ) )
		res.addState( q );

	// step 2
	for ( const auto & q : fsm.getInitialStates ( ) )
		res.addTransition ( q0, q );

	for ( const auto & t : fsm.getTransitions ( ) )
		res.addTransition ( t.first.first, t.first.second, t.second );

	// step 4, 5
	res.setFinalStates ( fsm.getFinalStates ( ) );

	return res;
}

template < class T >
ext::require < isDFA < T > || isNFA < T > || isEpsilonNFA < T > || isExtendedNFA < T > || isCompactNFA < T >, T > SingleInitialStateEpsilonTransition::convert ( const T & fsm ) {
	return fsm;
}

} /* namespace simplify */

} /* namespace automaton */

