/*
 * SuffixArrayFromBWT.h
 *
 *  Created on: 17. 02. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <string/LinearStringTerminatingSymbolBWT.h>
#include <indexes/stringology/SuffixArray.h>
#include <indexes/stringology/BWTAuxiliaryData.h>
#include "BWTAuxiliaryDataConstruct.h"

namespace stringology {

namespace indexing {

class SuffixArrayFromBWT {
    /** Postprocess decodedData to remove trailing terminating symbol and create resulting LinearString. */
    template < class SymbolType >
    static string::LinearString < SymbolType > removeTerminatingSymbol ( ext::set < SymbolType > alphabet,
                                                                         const SymbolType & terminatingSymbol,
                                                                         ext::vector < SymbolType > && decodedData );

public:
    /**
     * Construct a suffix array of the decoded string from the transformed string. The terminating symbol is removed in the process and the suffix array is shifted accordingly.
     *
     * @tparam SymbolType
     * @param str the input BWT-encoded string
     * @return the suffix array of the original (decoded) string
     */
    template < class SymbolType >
    static indexes::stringology::SuffixArray < SymbolType >
    construct ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str ) {
        return construct ( str, stringology::indexing::BWTAuxiliaryDataConstruct::construct ( str,
                                                                                              indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM));
    }

    /**
     * Construct a suffix array of the decoded string from the transformed string, reusing precomputed auxiliary data.. The terminating symbol is removed in the process and the suffix array is shifted accordingly.
     * Required data types are W_FORWARD_DECODE and M_OCC_PREFIX_SUM.
     *
     * @tparam SymbolType
     * @param str the input BWT-encoded string
     * @param data precomputed auxiliary data
     * @return the suffix array of the original (decoded) string
     */
    template < class SymbolType >
    static indexes::stringology::SuffixArray < SymbolType >
    construct ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str,
                const indexes::stringology::BWTAuxiliaryData < SymbolType > & data );
};

template < class SymbolType >
indexes::stringology::SuffixArray < SymbolType >
SuffixArrayFromBWT::construct ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str,
                                     const indexes::stringology::BWTAuxiliaryData < SymbolType > & data ) {
    if ( ! data.isDataTypePresent ( indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM ))
        throw exception::CommonException ( "Construction of suffix array from BWT string requires M array to be present." );

    measurements::start ( "SuffixArrayFromBWT", measurements::Type::ALGORITHM );
    // object::Object doesn't have default constructor, so we build the string in reverse and then flip it
    ext::vector < SymbolType > decodedData;
    ext::vector < unsigned > saData ( str.size ( ) - 1 );
    unsigned i = data.getTerminatingSymbolIndex ( );
    for ( size_t j = str.size ( ); j -- > 0; ) {
        const auto & symbol = str.getContent ( )[ i ];
        decodedData.push_back ( symbol );
        if ( i != 0 ) // remove terminating symbol from head of suffix array
            saData[ i - 1 ] = (j + 1) % str.size ( );
        i = data.getCPrefixOcc ( )[ i ] + data.getMOccPrefixSum ( ).at ( symbol );
    }
    std::reverse ( decodedData.begin ( ), decodedData.end ( )); // workaround
    measurements::end();

    return indexes::stringology::SuffixArray < SymbolType > ( saData,
                                                              removeTerminatingSymbol ( str.getAlphabet ( ),
                                                                                        str.getTerminatingSymbol ( ),
                                                                                        std::move ( decodedData )));
}

template < class SymbolType >
string::LinearString < SymbolType > SuffixArrayFromBWT::removeTerminatingSymbol ( ext::set < SymbolType > alphabet,
                                                                                       const SymbolType & terminatingSymbol,
                                                                                       ext::vector < SymbolType > && decodedData ) {
    decodedData.pop_back ( );
    alphabet.erase ( terminatingSymbol );
    return string::LinearString < SymbolType > ( alphabet, decodedData );
}

} /* namespace indexing */

} /* namespace stringology */

