/*
 * ExactSubtreeMatchingAutomaton.cpp
 *
 *  Created on: 9. 2. 2014
 *      Author: Jan Travnicek
 */

#include "ExactSubtreeMatchingAutomaton.h"
#include <tree/ranked/PrefixRankedBarTree.h>
#include <tree/ranked/PrefixRankedTree.h>
#include <tree/ranked/RankedTree.h>

#include <automaton/PDA/InputDrivenNPDA.h>
#include <automaton/TA/NFTA.h>

#include <alphabet/BottomOfTheStackSymbol.h>

#include <deque>

namespace arbology {

namespace exact {

automaton::Automaton ExactSubtreeMatchingAutomaton::construct ( const tree::Tree & pattern ) {
	return dispatch ( pattern.getData ( ) );
}

automaton::InputDrivenNPDA < > ExactSubtreeMatchingAutomaton::construct ( const tree::PrefixRankedTree & pattern ) {
	automaton::InputDrivenNPDA < > res ( label::labelFrom ( 0 ), alphabet::symbolFrom ( 'S' ) );

	for ( const std::ranked_symbol < > & symbol : pattern.getAlphabet ( ) ) {
		res.addInputSymbol ( alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ) );
		res.setPushdownStoreOperation ( alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), std::vector < alphabet::Symbol > ( 1, alphabet::symbolFrom ( 'S' ) ), std::vector < alphabet::Symbol > ( symbol.getRank ( ).getData ( ), alphabet::symbolFrom ( 'S' ) ) );
	}

	for ( const std::ranked_symbol < > & symbol : pattern.getAlphabet ( ) ) {
		res.addTransition ( label::labelFrom ( 0 ), alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), label::labelFrom ( 0 ) );
	}

	int i = 1;

	for ( const std::ranked_symbol < > & symbol : pattern.getContent ( ) ) {
		res.addState ( label::labelFrom ( i ) );
		res.addTransition ( label::labelFrom ( i - 1 ), alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), label::labelFrom ( i ) );
		i++;
	}

	res.addFinalState ( label::labelFrom ( i - 1 ) );
	return res;
}

auto ExactSubtreeMatchingAutomatonPrefixRankedTree = ExactSubtreeMatchingAutomaton::RegistratorWrapper < automaton::InputDrivenNPDA < >, tree::PrefixRankedTree > ( ExactSubtreeMatchingAutomaton::construct );

automaton::InputDrivenNPDA < > ExactSubtreeMatchingAutomaton::construct ( const tree::PrefixRankedBarTree & pattern ) {
	automaton::InputDrivenNPDA < > res ( label::labelFrom ( 0 ), alphabet::Symbol { alphabet::BottomOfTheStackSymbol::BOTTOM_OF_THE_STACK } );

	res.setPushdownStoreAlphabet ( { alphabet::Symbol { alphabet::BottomOfTheStackSymbol::BOTTOM_OF_THE_STACK }, alphabet::symbolFrom ( 'S' ) } );

	for ( const std::ranked_symbol < > & symbol : pattern.getAlphabet ( ) ) {
		res.addInputSymbol ( alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ) );

		if ( pattern.getBars ( ).count ( symbol ) )
			res.setPushdownStoreOperation ( alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), std::vector < alphabet::Symbol > ( 1, alphabet::symbolFrom ( 'S' ) ), std::vector < alphabet::Symbol > { } );
		else
			res.setPushdownStoreOperation ( alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), std::vector < alphabet::Symbol > { }, std::vector < alphabet::Symbol > ( 1, alphabet::symbolFrom ( 'S' ) ) );
	}

	for ( const std::ranked_symbol < > & symbol : pattern.getAlphabet ( ) ) {
		res.addTransition ( label::labelFrom ( 0 ), alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), label::labelFrom ( 0 ) );
	}

	int i = 1;

	for ( const std::ranked_symbol < > & symbol : pattern.getContent ( ) ) {
		res.addState ( label::labelFrom ( i ) );
		res.addTransition ( label::labelFrom ( i - 1 ), alphabet::Symbol ( alphabet::RankedSymbol < > { symbol } ), label::labelFrom ( i ) );
		i++;
	}

	res.addFinalState ( label::labelFrom ( i - 1 ) );
	return res;
}

auto ExactSubtreeMatchingAutomatonPrefixRankedBarTree = ExactSubtreeMatchingAutomaton::RegistratorWrapper < automaton::InputDrivenNPDA < >, tree::PrefixRankedBarTree > ( ExactSubtreeMatchingAutomaton::construct );

label::Label constructRecursive ( const std::tree < std::ranked_symbol < > > & node, automaton::NFTA & res, int & nextState ) {
	std::vector < label::Label > states;

	states.reserve ( node.getData ( ).getRank ( ).getData ( ) );

	for ( const std::tree < std::ranked_symbol < > > & child : node.getChildren ( ) )
		states.push_back ( constructRecursive ( child, res, nextState ) );

	label::Label state = label::labelFrom ( nextState++ );
	res.addState ( state );
	res.addTransition ( node.getData ( ), states, state );
	return state;
}

automaton::NFTA ExactSubtreeMatchingAutomaton::construct ( const tree::RankedTree < > & pattern ) {
	automaton::NFTA res;

	res.setInputAlphabet ( pattern.getAlphabet ( ) );
	int nextState = 0;
	res.addFinalState ( constructRecursive ( pattern.getContent ( ), res, nextState ) );
	return res;
}

auto ExactSubtreeMatchingAutomatonRankedTree = ExactSubtreeMatchingAutomaton::RegistratorWrapper < automaton::NFTA, tree::RankedTree < > > ( ExactSubtreeMatchingAutomaton::construct );

} /* namespace exact */

} /* namespace arbology */