/*
 * Result.h
 *
 *  Created on: 9. 2. 2014
 *      Author: Jan Travnicek
 */

#ifndef _AUTOMATON_RESULT_H__
#define _AUTOMATON_RESULT_H__

#include <string/LinearString.h>
#include <tree/ranked/RankedTree.h>
#include <label/FailStateLabel.h>

#include "Run.h"
#include <automaton/FSM/DFA.h>
#include <automaton/TA/DFTA.h>
#include <automaton/PDA/InputDrivenDPDA.h>
#include <automaton/PDA/VisiblyPushdownDPDA.h>
#include <automaton/PDA/RealTimeHeightDeterministicDPDA.h>
#include <automaton/PDA/DPDA.h>

#include <alib/deque>

namespace automaton {

namespace run {

/**
 * \brief
 * Implementation of automaton run over its input returning the reached state.
 */
class Result {
public:
	/**
	 * General automaton run implementation resulting in the reached state. Generic fail state is returned if the automaton's transition function was not defined for the input.
	 *
	 * \tparam SymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class SymbolType, class StateType >
	static StateType result ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of tree nodes and terminal symbols of the runned automaton
	 * \tparam RankType type of ranks of tree nodes and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class SymbolType, class StateType >
	static StateType result ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string );

	/**
	 * General automaton run implementation resulting in the reached state. Given fail state is returned if the automaton's transition function was not defined for the input.
	 *
	 * \tparam SymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class SymbolType, class StateType >
	static StateType result ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string, const StateType & failLabel );

	/**
	 * \override
	 *
	 * \tparam SymbolType type of symbols of tree nodes and terminal symbols of the runned automaton
	 * \tparam RankType type of ranks of tree nodes and terminal symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class SymbolType, class StateType >
	static StateType result ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree, const StateType & failLabel );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel );

	/**
	 * \override
	 *
	 * \tparam InputSymbolType type of symbols of the string and terminal symbols of the runned automaton
	 * \tparam PushdownStoreSymbolType type of pushdown store symbols of the runned automaton
	 * \tparam StateType type of states of the runned automaton
	 *
	 * \param automaton the runned automaton
	 * \param string the input of the automaton
	 * \param the fail state to use when transition function is not defined for the input
	 *
	 * \return state where the run stopped
	 */
	template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
	static StateType result ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel );
};

template < class SymbolType, class StateType >
StateType Result::result ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string ) {
	return result ( automaton, string, label::FailStateLabel::instance < StateType > ( ) );
}

template < class SymbolType, class StateType >
StateType Result::result ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree ) {
	return result ( automaton, tree, label::FailStateLabel::instance < StateType > ( ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	return result ( automaton, string, label::FailStateLabel::instance < StateType > ( ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	return result ( automaton, string, label::FailStateLabel::instance < StateType > ( ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	return result ( automaton, string, label::FailStateLabel::instance < StateType > ( ) );
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string ) {
	return result ( automaton, string, label::FailStateLabel::instance < StateType > ( ) );
}

template < class SymbolType, class StateType >
StateType Result::result ( const automaton::DFA < SymbolType, StateType > & automaton, const string::LinearString < SymbolType > & string, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned > > res = Run::calculateState ( automaton, string );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

template < class SymbolType, class StateType >
StateType Result::result ( const automaton::DFTA < SymbolType, StateType > & automaton, const tree::RankedTree < SymbolType > & tree, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned > > res = Run::calculateState ( automaton, tree );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::InputDrivenDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::VisiblyPushdownDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::RealTimeHeightDeterministicDPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

template < class InputSymbolType, class PushdownStoreSymbolType, class StateType >
StateType Result::result ( const automaton::DPDA < InputSymbolType, PushdownStoreSymbolType, StateType > & automaton, const string::LinearString < InputSymbolType > & string, const StateType & failLabel ) {
	ext::tuple < bool, StateType, ext::set < unsigned >, ext::deque < PushdownStoreSymbolType > > res = Run::calculateState ( automaton, string );

	if ( std::get < 0 > ( res ) ) return std::get < 1 > ( res );

	return failLabel;
}

} /* namespace run */

} /* namespace automaton */

#endif /* _AUTOMATON_RESULT_H__ */
