/*
 * BWTBoyerMoore.h
 *
 *  Created on: 19. 02. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <sstream>
#include <alib/measure>
#include <string/LinearStringTerminatingSymbolBWT.h>
#include <stringology/indexing/BWTAuxiliaryDataConstruct.h>
#include <string/properties/BadCharacterShiftTable.h>
#include <string/properties/GoodSuffixShiftTable.h>

namespace stringology {

namespace exact {

class BWTBoyerMoore {
public:
    /**
     * Run Boyer-Moore algorithm to find occurrences of pattern in subject (a BWT encoded string).
     *
     * @tparam SymbolType
     * @param subject the input BWT-encoded string
     * @param pattern the pattern to search for
     * @return indices of occurrences of pattern in the original (decoded) text, starting at 0
     */
    template < class SymbolType >
    static ext::set < unsigned > match ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & subject,
                                         const string::LinearString < SymbolType > & pattern ) {
        return match ( subject, pattern,
                       stringology::indexing::BWTAuxiliaryDataConstruct::construct ( subject,
                                                                                     indexes::stringology::BWTAuxiliaryDataType::HR_TEXT_TO_FIRST ));
    }

    /**
     * Run Boyer-Moore to find occurrences of pattern in subject (a BWT encoded string), reusing precomputed auxiliary data.
     * Required data type is HR_TEXT_TO_FIRST.
     *
     * @tparam SymbolType
     * @param subject the input BWT-encoded string
     * @param pattern the pattern to search for
     * @param data precomputed auxiliary data
     * @return indices of occurrences of pattern in the original (decoded) text, starting at 0
     */
    template < class SymbolType >
    static ext::set < unsigned > match ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & subject,
                                         const string::LinearString < SymbolType > & pattern,
                                         const indexes::stringology::BWTAuxiliaryData < SymbolType > & data );
};

template < class SymbolType >
ext::set < unsigned > BWTBoyerMoore::match ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & subject,
                                             const string::LinearString < SymbolType > & pattern,
                                             const indexes::stringology::BWTAuxiliaryData < SymbolType > & data ) {
    if ( ! data.isDataTypePresent ( indexes::stringology::BWTAuxiliaryDataType::HR_TEXT_TO_FIRST ))
        throw exception::CommonException ( "BWT Boyer-Moore requires Hr array to be present." );

    if ( pattern.empty ( ))
        throw exception::CommonException ( "Pattern cannot be empty." );

    measurements::start ( "BCS & GSS construction", measurements::Type::PREPROCESS );
    // NOTE: the subjects alphabet must be a subset or equal to the pattern
    ext::map < SymbolType, size_t > bcs = string::properties::BadCharacterShiftTable::bcs ( pattern );
    ext::vector < size_t > gss = string::properties::GoodSuffixShiftTable::gss ( pattern );
    measurements::end ( );

    // this function allows random access to the subject, like it was already decoded
    auto getSymbolAt = [ & ] ( size_t index ) {
        // We can exploit a property in the cyclic shift matrix and use hrTextToFirst to get an index in the encoded
        // string (simulating a "textToLast" array). By moving by one symbol in the original string, the first symbol
        // becomes the last. Thanks to this property, we can ask for the "first" index of the next symbol and we get
        // the "last" index of the current symbol.

        size_t lastIndex = data.getHrTextToFirst ( )[ ( index + 1 ) % subject.size ( ) ];
        return subject.getContent ( )[ lastIndex ];
    };

    measurements::start ( "BWTBoyerMoore", measurements::Type::ALGORITHM );
    ext::set < unsigned > matches;
    size_t k = 0;
    while ( k + pattern.size ( ) <= subject.size ( ) - 1 ) { // - 1: skip terminating symbol
        size_t i = pattern.size ( );
        while ( i > 0 && getSymbolAt ( k + i - 1 ) == pattern.getContent ( )[ i - 1 ] )
            i --;
        if ( i == 0 ) {
            matches.insert ( k );
            k += gss[ pattern.size ( ) - i ];
        } else {
            size_t sg = gss[ pattern.size ( ) - i ];
            size_t sb = bcs[ getSymbolAt ( k + pattern.size ( ) - 1 ) ];
            k += std::max ( sg, sb );
        }
    }
    measurements::end ( );

    return matches;
}

} /* namespace exact */

} /* namespace stringology */


