/*
 * BadCharacterShiftTable.h
 *
 *  Created on: 6. 3. 2018
 *	  Author: Michal Cvach
 */

#pragma once

#include <tree/ranked/PrefixRankedBarPattern.h>
#include <tree/ranked/PrefixRankedBarNonlinearPattern.h>

#include "LastVariableOffsetBack.h"

#include <alib/set>
#include <alib/map>

namespace tree {

namespace properties {

/**
* BadCharacterShiftTable for the QuickSearch algorithm for tree pattern matching.
*/
class QuickSearchBadCharacterShiftTable {
public:
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedBarPattern < SymbolType > & pattern );
	template < class SymbolType >
	static ext::map < common::ranked_symbol < SymbolType >, size_t > bcs ( const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern );

};

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > QuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedBarPattern < SymbolType > & pattern ) {
	return bcs ( tree::PrefixRankedBarNonlinearPattern < SymbolType > ( pattern ) );
}

template < class SymbolType >
ext::map < common::ranked_symbol < SymbolType >, size_t > QuickSearchBadCharacterShiftTable::bcs ( const tree::PrefixRankedBarNonlinearPattern < SymbolType > & pattern ) {
	const ext::set < common::ranked_symbol < SymbolType > > & alphabet = pattern.getAlphabet ( );

	ext::map < common::ranked_symbol < SymbolType >, size_t > bcs;

	// initialisation of bcs table to the size of the pattern plus one
	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( symbol == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( symbol ) || symbol == pattern.getVariablesBar ( ) )
			continue;

		bcs.insert ( std::make_pair ( symbol, pattern.getContent ( ).size ( ) + 1 ) );
	}

	// find the distance between the end of the pattern and the index
	// of the last symbol representing the variable
	size_t lastSOffset = LastVariableOffsetBack::offset ( pattern );

	// limit the shift by occurrence of the last variable

	for ( const common::ranked_symbol < SymbolType > & symbol : alphabet ) {
		if ( symbol == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( symbol ) || symbol == pattern.getVariablesBar ( ) )
			continue;

		size_t tmp = lastSOffset + 1;

		if ( ! pattern.getBars ( ).count ( symbol ) )
			// size of the smallest subtree containing given terminal depend
			// on the arity of the terminal
			tmp += ( size_t ) symbol.getRank ( ) * 2;
		else
			// bar symbols match the variable bar which is one symbol after
			// the last variable, conditioned because of the case S S| where
			// the -1 would cause shift by 0 -- illegal
			tmp -= 1;

		if ( bcs[symbol] > tmp )
			bcs[symbol] = tmp;
	}

	// limit the shift by position of symbols within the pattern
	for ( unsigned i = 0; i < pattern.getContent ( ).size ( ); i++ ) {
		if ( pattern.getContent ( )[i] == pattern.getSubtreeWildcard ( ) || pattern.getNonlinearVariables ( ).count ( pattern.getContent ( )[i] ) || pattern.getContent ( )[i] == pattern.getVariablesBar ( ) )
			continue;

		size_t tmp = pattern.getContent ( ).size ( ) - i;

		if ( bcs[pattern.getContent ( )[i]] > tmp )
			bcs[pattern.getContent ( )[i]] = tmp;
	}

	return bcs;
}

} /* namespace properties */

} /* namespace tree */

