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

#ifndef _EXACT_PATTERN_MATCH_H_
#define _EXACT_PATTERN_MATCH_H_

#include <alib/set>
#include <alib/tree>
#include <alib/deque>
#include <alib/foreach>

#include <tree/ranked/RankedTree.h>
#include <tree/ranked/RankedPattern.h>
#include <tree/ranked/RankedNonlinearPattern.h>
#include <tree/ranked/PrefixRankedTree.h>
#include <tree/ranked/PrefixRankedPattern.h>
#include <tree/ranked/PrefixRankedNonlinearPattern.h>
#include <tree/ranked/PrefixRankedBarTree.h>
#include <tree/ranked/PrefixRankedBarPattern.h>
#include <tree/ranked/PrefixRankedBarNonlinearPattern.h>
#include <tree/unranked/UnrankedTree.h>
#include <tree/unranked/UnrankedPattern.h>

#include <tree/properties/SubtreeJumpTable.h>
#include <tree/properties/ExactSubtreeRepeatsNaive.h>
#include <tree/exact/ForwardOccurrenceTest.h>

namespace arbology {

namespace exact {

class ExactPatternMatch {
public:
	/**
	 * Performs conversion.
	 * @return left regular grammar equivalent to source automaton.
	 */
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::UnrankedTree < SymbolType > & subject, const tree::UnrankedPattern < SymbolType > & pattern );

	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::RankedTree < SymbolType > & subject, const tree::RankedPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::RankedTree < SymbolType > & subject, const tree::RankedNonlinearPattern < SymbolType > & pattern );

	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern );

	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::set < unsigned > match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern );

private:
	template < class SymbolType >
	static bool matchHelper ( const ext::tree < SymbolType > & subject, const ext::tree < SymbolType > & pattern, const SymbolType & subtreeVariable );
	template < class SymbolType >
	static bool matchHelper ( const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable );
	template < class SymbolType >
	static bool matchHelper ( const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable, const ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables, const ext::tree < common::ranked_symbol < unsigned > > & repeats, ext::map < common::ranked_symbol < SymbolType >, unsigned > & variablesSetting );

	template < class SymbolType >
	static void matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < SymbolType > & subject, const ext::tree < SymbolType > & pattern, const SymbolType & subtreeVariable );
	template < class SymbolType >
	static void matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable );
	template < class SymbolType >
	static void matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable, const ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables, const ext::tree < common::ranked_symbol < unsigned > > & repeats );

};

template < class SymbolType >
bool ExactPatternMatch::matchHelper ( const ext::tree < SymbolType > & subject, const ext::tree < SymbolType > & pattern, const SymbolType & subtreeVariable ) {
	if ( pattern.getData ( ) == subtreeVariable ) return true;

	if ( subject.getData ( ) != pattern.getData ( ) ) return false;

	if ( subject.getChildren ( ).size ( ) != pattern.getChildren ( ).size ( ) ) return false;

	for ( const ext::tuple < const ext::tree < SymbolType > &, const ext::tree < SymbolType > & > & childs : ext::make_tuple_foreach ( subject.getChildren ( ), pattern.getChildren ( ) ) )
		if ( !matchHelper ( std::get < 0 > ( childs ), std::get < 1 > ( childs ), subtreeVariable ) ) return false;

	return true;
}

template < class SymbolType >
bool ExactPatternMatch::matchHelper ( const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable ) {
	if ( pattern.getData ( ) == subtreeVariable ) return true;

	if ( subject.getData ( ) != pattern.getData ( ) ) return false;

	 // ranked symbols are the same; test for number of children is not needed
	for ( const ext::tuple < const ext::tree < common::ranked_symbol < SymbolType > > &, const ext::tree < common::ranked_symbol < SymbolType > > & > & childs : ext::make_tuple_foreach ( subject.getChildren ( ), pattern.getChildren ( ) ) )
		if ( !matchHelper ( std::get < 0 > ( childs ), std::get < 1 > ( childs ), subtreeVariable ) ) return false;

	return true;
}

template < class SymbolType >
bool ExactPatternMatch::matchHelper ( const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable, const ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables, const ext::tree < common::ranked_symbol < unsigned > > & repeats, ext::map < common::ranked_symbol < SymbolType >, unsigned > & variablesSetting ) {
	if ( pattern.getData ( ) == subtreeVariable ) return true;

	if ( nonlinearVariables.count ( pattern.getData ( ) ) ) {
		auto setting = variablesSetting.find ( pattern.getData ( ) );

		if ( setting != variablesSetting.end ( ) ) return repeats.getData ( ).getSymbol ( ) == setting->second;

		variablesSetting.insert ( std::make_pair ( pattern.getData ( ), repeats.getData ( ).getSymbol ( ) ) );

		return true;
	}

	if ( subject.getData ( ) != pattern.getData ( ) ) return false;

	 // ranked symbols are the same; test for number of children is not needed
	for ( const ext::tuple < const ext::tree < common::ranked_symbol < SymbolType > > &, const ext::tree < common::ranked_symbol < SymbolType > > &, const ext::tree < common::ranked_symbol < unsigned > > & > & childs : ext::make_tuple_foreach ( subject.getChildren ( ), pattern.getChildren ( ), repeats.getChildren ( ) ) )
		if ( !matchHelper ( std::get < 0 > ( childs ), std::get < 1 > ( childs ), subtreeVariable, nonlinearVariables, std::get < 2 > ( childs ), variablesSetting ) ) return false;

	return true;
}

template < class SymbolType >
void ExactPatternMatch::matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < SymbolType > & subject, const ext::tree < SymbolType > & pattern, const SymbolType & subtreeVariable ) {
	if ( matchHelper ( subject, pattern, subtreeVariable ) ) occ.insert ( index );

	index++;

	for ( const ext::tree < SymbolType > & child : subject.getChildren ( ) )
		matchInternal ( index, occ, child, pattern, subtreeVariable );
}

template < class SymbolType >
void ExactPatternMatch::matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable ) {
	if ( matchHelper ( subject, pattern, subtreeVariable ) ) occ.insert ( index );

	index++;

	for ( const ext::tree < common::ranked_symbol < SymbolType > > & child : subject.getChildren ( ) )
		matchInternal ( index, occ, child, pattern, subtreeVariable );
}

template < class SymbolType >
void ExactPatternMatch::matchInternal ( unsigned & index, ext::set < unsigned > & occ, const ext::tree < common::ranked_symbol < SymbolType > > & subject, const ext::tree < common::ranked_symbol < SymbolType > > & pattern, const common::ranked_symbol < SymbolType > & subtreeVariable, const ext::set < common::ranked_symbol < SymbolType > > & nonlinearVariables, const ext::tree < common::ranked_symbol < unsigned > > & repeats ) {
	ext::map < common::ranked_symbol < SymbolType >, unsigned > variablesSetting;

	if ( matchHelper ( subject, pattern, subtreeVariable, nonlinearVariables, repeats, variablesSetting ) ) occ.insert ( index );

	index++;

	for ( const ext::tuple < const ext::tree < common::ranked_symbol < SymbolType > > &, const ext::tree < common::ranked_symbol < unsigned > > & > & childs : ext::make_tuple_foreach ( subject.getChildren ( ), repeats.getChildren ( ) ) )
		matchInternal ( index, occ, std::get < 0 > ( childs ), pattern, subtreeVariable, nonlinearVariables, std::get < 1 > ( childs ) );
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::UnrankedTree < SymbolType > & subject, const tree::UnrankedPattern < SymbolType > & pattern ) {
	unsigned i = 0;
	ext::set < unsigned > occ;

	matchInternal ( i, occ, subject.getContent ( ), pattern.getContent ( ), pattern.getSubtreeWildcard ( ) );
	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::RankedTree < SymbolType > & subject, const tree::RankedPattern < SymbolType > & pattern ) {
	unsigned i = 0;
	ext::set < unsigned > occ;

	matchInternal ( i, occ, subject.getContent ( ), pattern.getContent ( ), pattern.getSubtreeWildcard ( ) );
	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::RankedTree < SymbolType > & subject, const tree::RankedNonlinearPattern < SymbolType > & pattern ) {
	unsigned i = 0;
	ext::set < unsigned > occ;

	tree::RankedTree < unsigned > repeats = tree::properties::ExactSubtreeRepeatsNaive::repeats ( subject );

	matchInternal ( i, occ, subject.getContent ( ), pattern.getContent ( ), pattern.getSubtreeWildcard ( ), pattern.getNonlinearVariables ( ), repeats.getContent ( ) );
	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedPattern < SymbolType > & pattern ) {
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );

	ext::set < unsigned > occ;

	for ( unsigned i = 0; i + pattern.getContent ( ).size ( ) <= subject.getContent ( ).size ( ); i++ ) {
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, pattern, i );

		if ( j == pattern.getContent ( ).size ( ) )
			occ.insert ( i );
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::PrefixRankedTree < SymbolType > & subject, const tree::PrefixRankedNonlinearPattern < SymbolType > & pattern ) {
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );
	tree::PrefixRankedTree < unsigned > repeats = tree::properties::ExactSubtreeRepeatsNaive::repeats ( subject );

	ext::set < unsigned > occ;

	for ( unsigned i = 0; i + pattern.getContent ( ).size ( ) <= subject.getContent ( ).size ( ); i++ ) {
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, repeats, pattern, i );

		if ( j == pattern.getContent ( ).size ( ) )
			occ.insert ( i );
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarPattern < SymbolType > & pattern ) {
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );

	ext::set < unsigned > occ;

	for ( unsigned i = 0; i + pattern.getContent ( ).size ( ) <= subject.getContent ( ).size ( ); i++ ) {
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, pattern, i );

		if ( j == pattern.getContent ( ).size ( ) )
			occ.insert ( i );
	}

	return occ;
}

template < class SymbolType >
ext::set < unsigned > ExactPatternMatch::match ( const tree::PrefixRankedBarTree < SymbolType > & subject, const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern ) {
	ext::vector < int > subjectSubtreeJumpTable = tree::properties::SubtreeJumpTable::compute ( subject );
	tree::PrefixRankedBarTree < unsigned > repeats = tree::properties::ExactSubtreeRepeatsNaive::repeats ( subject );

	ext::set < unsigned > occ;

	for ( unsigned i = 0; i + pattern.getContent ( ).size ( ) <= subject.getContent ( ).size ( ); i++ ) {
		unsigned j = tree::exact::ForwardOccurrenceTest::occurrence ( subject, subjectSubtreeJumpTable, repeats, pattern, i );

		if ( j == pattern.getContent ( ).size ( ) )
			occ.insert ( i );
	}

	return occ;
}

} /* namespace exact */

} /* namespace arbology */

#endif /* _EXACT_PATTERN_MATCH_H_ */
