/*
 * Author: Radovan Cerveny
 */

#ifndef STRINGOLOGY_BACKWARD_ORACLE_MATCHING_H__
#define STRINGOLOGY_BACKWARD_ORACLE_MATCHING_H__

#include <alib/set>

#include <string/LinearString.h>
#include <automaton/FSM/DFA.h>

#include <stringology/properties/BackboneLength.h>

namespace stringology {

namespace query {

/**
 * Implementation of Backward Oracle Matching.
 */
class BackwardOracleMatching {
public:
	/**
	 * Search for pattern in linear string.
	 * @return set set of occurences
	 */
	template < class SymbolType, class StateType >
	static ext::set < unsigned > match ( const string::LinearString < SymbolType > & subject, const automaton::DFA < SymbolType, StateType > & factorOracle );

};

template < class SymbolType, class StateType >
ext::set < unsigned > BackwardOracleMatching::match ( const string::LinearString < SymbolType > & subject, const automaton::DFA < SymbolType, StateType > & factorOracle ) {
	ext::set < unsigned > occ;

	size_t patternSize = stringology::properties::BackboneLength::length ( factorOracle );
	size_t subjectSize = subject.getContent ( ).size ( );

	bool fail;
	size_t posInSubject = 0;

	while ( posInSubject + patternSize <= subjectSize ) {

		StateType currentState = factorOracle.getInitialState ( );

		size_t posInPattern = patternSize;

		fail = false;
		while ( posInPattern > 0 && ! fail ) {
			auto transition = factorOracle.getTransitions ( ).find ( { currentState, subject.getContent ( ).at ( posInSubject + posInPattern - 1 ) } );

			if ( transition == factorOracle.getTransitions ( ).end ( ) )
				fail = true;
			else
				currentState = transition->second;

			posInPattern--;
		}

		if ( ! fail )
			 // Yay, there is match!!!
			occ.insert ( posInSubject );

		posInSubject += posInPattern + 1;
	}

	return occ;
}

} /* namespace query */

} /* namespace stringology */

#endif /* STRINGOLOGY_BACKWARD_ORACLE_MATCHING_H__ */
