/*
 * ToAutomatonThompson.cpp
 *
 *  Created on: 11. 1. 2014
 *	  Author: Tomas Pecka
 */
#include "ToAutomatonThompson.h"
#include <regexp/unbounded/UnboundedRegExpElements.h>
#include <regexp/formal/FormalRegExpElements.h>
#include <registration/AlgoRegistration.hpp>

namespace regexp {

namespace convert {

automaton::EpsilonNFA < > regexp::convert::ToAutomatonThompson::convert(const regexp::FormalRegExp < > & regexp) {
	//FIXME use actual algorithms that implement product alternation and iteration of re over automata and remove terrible TERRIBLE hack with dummy initial state
	int nextState = 1;
	const DefaultStateType * headArg = nullptr;
	const DefaultStateType * tailArg = nullptr;

	automaton::EpsilonNFA < > automaton ( DefaultStateType ( 0 ) );
	automaton.setInputAlphabet(regexp.getAlphabet());

	regexp.getRegExp().getStructure().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);

	automaton.setInitialState ( * headArg );
	automaton.setFinalStates ( ext::set < DefaultStateType > { * tailArg } );

	automaton.removeState(DefaultStateType(0));

	return automaton;
}

automaton::EpsilonNFA < > regexp::convert::ToAutomatonThompson::convert(const regexp::UnboundedRegExp < > & regexp) {
	//FIXME use actual algorithms that implement product alternation and iteration of re over automata and remove terrible TERRIBLE hack with dummy initial state
	int nextState = 1;
	const DefaultStateType * headArg = nullptr;
	const DefaultStateType * tailArg = nullptr;

	automaton::EpsilonNFA < > automaton ( DefaultStateType ( 0 ) );
	automaton.setInputAlphabet(regexp.getAlphabet());

	regexp.getRegExp().getStructure().accept < void, regexp::convert::ToAutomatonThompson::Unbounded, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);

	automaton.setInitialState ( * headArg );
	automaton.setFinalStates ( ext::set < DefaultStateType > { * tailArg } );

	automaton.removeState(DefaultStateType(0));

	return automaton;
}

// ----------------------------------------------------------------------------

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpAlternation < DefaultSymbolType > & alternation, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	alternation.getLeftElement().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	automaton.addTransition(head, *headArg);
	automaton.addTransition(*tailArg, tail);

	alternation.getRightElement().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	automaton.addTransition(head, *headArg);
	automaton.addTransition(*tailArg, tail);

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpConcatenation < DefaultSymbolType > & concatenation, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	concatenation.getLeftElement().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	const DefaultStateType* leftHead = headArg;
	const DefaultStateType* leftTail = tailArg;

	concatenation.getRightElement().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	automaton.addTransition(*leftTail, *headArg);

	headArg = &(*automaton.getStates().find(*leftHead));
	// tailArg = tailArg;
}

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpIteration < DefaultSymbolType > & iteration, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	iteration.getElement().accept < void, regexp::convert::ToAutomatonThompson::Formal, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	automaton.addTransition(head, *headArg);
	automaton.addTransition(head, tail);
	automaton.addTransition(*tailArg, tail);
	automaton.addTransition(*tailArg, *headArg);

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpSymbol < DefaultSymbolType > & symbol, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	automaton.addTransition(head, symbol.getSymbol(), tail);
	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpEpsilon < DefaultSymbolType > &, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	automaton.addTransition(head, tail);
	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Formal::visit(const regexp::FormalRegExpEmpty < DefaultSymbolType > &, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

// ----------------------------------------------------------------------------

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpAlternation < DefaultSymbolType > & alternation, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	for(const UnboundedRegExpElement < DefaultSymbolType > & element : alternation.getElements()) {
		element.accept < void, regexp::convert::ToAutomatonThompson::Unbounded, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
		automaton.addTransition(head, *headArg);
		automaton.addTransition(*tailArg, tail);
	}

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpConcatenation < DefaultSymbolType > & concatenation, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	ext::vector<std::pair<const DefaultStateType*, const DefaultStateType*>> tails;
	for(const UnboundedRegExpElement < DefaultSymbolType > & element : concatenation.getElements()) {
		element.accept < void, regexp::convert::ToAutomatonThompson::Unbounded, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
		tails.push_back(std::make_pair(headArg, tailArg));
	}

	for(size_t i = 1; i < tails.size(); i++)
		automaton.addTransition(*tails[i-1].second, *tails[i].first);

	headArg = tails[0].first;
	tailArg = tails[tails.size()-1].second;
}

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpIteration < DefaultSymbolType > & iteration, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	iteration.getElement().accept < void, regexp::convert::ToAutomatonThompson::Unbounded, automaton::EpsilonNFA < > &, int &, const DefaultStateType * &, const DefaultStateType * & >(automaton, nextState, headArg, tailArg);
	automaton.addTransition(head, *headArg);
	automaton.addTransition(head, tail);
	automaton.addTransition(*tailArg, tail);
	automaton.addTransition(*tailArg, *headArg);

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpSymbol < DefaultSymbolType > & symbol, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	automaton.addTransition(head, symbol.getSymbol(), tail);
	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpEpsilon < DefaultSymbolType > &, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	automaton.addTransition(head, tail);
	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

void regexp::convert::ToAutomatonThompson::Unbounded::visit(const regexp::UnboundedRegExpEmpty < DefaultSymbolType > &, automaton::EpsilonNFA < > & automaton , int & nextState, const DefaultStateType * & headArg, const DefaultStateType * & tailArg) {
	DefaultStateType head = DefaultStateType(nextState++);
	DefaultStateType tail = DefaultStateType(nextState++);
	automaton.addState(head);
	automaton.addState(tail);

	headArg = &(*automaton.getStates().find(head));
	tailArg = &(*automaton.getStates().find(tail));
}

} /* namespace convert */

} /* namespace regexp */

namespace {

auto ToAutomatonThompsonFormalRegExp = registration::AbstractRegister < regexp::convert::ToAutomatonThompson, automaton::EpsilonNFA < >, const regexp::FormalRegExp < > & > ( regexp::convert::ToAutomatonThompson::convert, "regexp" ).setDocumentation (
"Implements conversion of regular expressions to finite automata usign Thompson's method of incremental construction.\n\
\n\
@param regexp the regexp to convert\n\
@return finite automaton accepting the language described by the original regular expression" );

auto ToAutomatonThompsonUnboundedRegExp = registration::AbstractRegister < regexp::convert::ToAutomatonThompson, automaton::EpsilonNFA < >, const regexp::UnboundedRegExp < > & > ( regexp::convert::ToAutomatonThompson::convert, "regexp" ).setDocumentation (
"Implements conversion of regular expressions to finite automata usign Thompson's method of incremental construction.\n\
\n\
@param regexp the regexp to convert\n\
@return finite automaton accepting the language described by the original regular expression" );

} /* namespace */
