/*
 * HammingSequenceMatchingAutomaton.h
 *
 *  Created on: 29. 3. 2018
 *      Author: Tomas Capek
 */

#ifndef _HAMMING_SEQUENCE_MATCHING_AUTOMATON_H__
#define _HAMMING_SEQUENCE_MATCHING_AUTOMATON_H__

#include <automaton/FSM/NFA.h>
#include <stringology/matching/HammingMatchingAutomaton.h>
#include <string/LinearString.h>
#include <string/WildcardLinearString.h>


namespace stringology {

namespace matching {

class HammingSequenceMatchingAutomaton {
public:
	/**
	 * Creates Hamming matching automata for sequence matching from LinearString.
	 *
	 * @return automata for aproximate sequence matching using Hamming method.
	 */
	template < class SymbolType >
	static automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int> > construct(const string::LinearString < SymbolType > & pattern, unsigned int allowed_errors);

	/**
	 * Creates Hamming matching automata for sequence matching from WildcardLinearString.
	 *
	 * @return automata for aproximate sequence matching using Hamming method.
	 */
	template < class SymbolType >
	static automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int> > construct(const string::WildcardLinearString < SymbolType > & pattern, unsigned int allowed_errors);

};

template < class SymbolType >
automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int> > HammingSequenceMatchingAutomaton::construct(const string::LinearString < SymbolType > & pattern, unsigned int allowed_errors) {
	automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int > > result = stringology::matching::HammingMatchingAutomaton::construct(pattern, allowed_errors);

	for (unsigned int j = 0; j<allowed_errors + 1; j++) {
		for (unsigned int i = j; i<pattern.getContent().size(); i++) {
			auto current_state = ext::make_pair(i, j);

			for (const SymbolType & symbol : pattern.getAlphabet()) {
				if (symbol != pattern.getContent()[i]) {
					result.addTransition(current_state, symbol, current_state);
				}
			}
		}
	}

	return result;
}

template < class SymbolType >
automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int> > HammingSequenceMatchingAutomaton::construct(const string::WildcardLinearString < SymbolType > & pattern, unsigned int allowed_errors) {
	automaton::NFA < SymbolType, ext::pair<unsigned int, unsigned int > > result = stringology::matching::HammingMatchingAutomaton::construct(pattern, allowed_errors);

	SymbolType wildcard = pattern.getWildcardSymbol();
	ext::set<SymbolType> alphabet_without_wildcard = pattern.getAlphabet();
	alphabet_without_wildcard.erase(wildcard);

	for (unsigned int j = 0; j<allowed_errors + 1; j++) {
		for (unsigned int i = j; i<pattern.getContent().size(); i++) {
			auto current_state = ext::make_pair(i, j);

			if (pattern.getContent()[i] != wildcard) {
				for (const SymbolType & symbol : alphabet_without_wildcard) {
					if (symbol != pattern.getContent()[i]) {
						result.addTransition(current_state, symbol, current_state);
					}
				}
			}
		}
	}

	return result;
}

} /* namespace matching */

} /* namespace stringology */

#endif /* _HAMMING_SEQUENCE_MATCHING_AUTOMATON_H__ */
