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

#pragma once

#include <string/LinearString.h>
#include <string/LinearStringTerminatingSymbolBWT.h>
#include <stringology/indexing/BWTAuxiliaryDataConstruct.h>

namespace stringology {

namespace transform {

class BWTDecodeNaive {
    /** 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:
    /**
     * Decode a BWT-encoded string and remove terminating symbol.
     *
     * @tparam SymbolType
     * @param str the input BWT-encoded string
     * @return the decoded string with removed terminating symbol
     */
    template < class SymbolType >
    static string::LinearString < SymbolType >
    decode ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str ) {
        return decode ( str, stringology::indexing::BWTAuxiliaryDataConstruct::construct ( str,
                                                                                           indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM ));
    }

    /**
      * Decode a BWT-encoded string and remove terminating symbol, reusing precomputed auxiliary data.
      * This implementation uses the M and C arrays. Required data type is M_OCC_PREFIX_SUM.
      *
      * @tparam SymbolType
      * @param str the input BWT-encoded string
      * @param data precomputed auxiliary data
      * @return the decoded string with removed terminating symbol
      */
    template < class SymbolType >
    static string::LinearString < SymbolType >
    decode ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str,
             const indexes::stringology::BWTAuxiliaryData < SymbolType > & data ) {
        if ( ! data.isDataTypePresent ( indexes::stringology::BWTAuxiliaryDataType::M_OCC_PREFIX_SUM ))
            throw exception::CommonException ( "Decoding BWT string requires M array to be present." );

        measurements::start ( "BWTDecodeNaive", 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;
        unsigned i = data.getTerminatingSymbolIndex ( );
        for ( size_t j = str.size ( ); j -- > 0; ) {
            const auto & symbol = str.getContent ( )[ i ];
            decodedData.push_back ( symbol );
            i = data.getCPrefixOcc ( )[ i ] + data.getMOccPrefixSum ( ).at ( symbol );
        }
        std::reverse ( decodedData.begin ( ), decodedData.end ( ));
        measurements::end ( );

        return removeTerminatingSymbol ( str.getAlphabet ( ), str.getTerminatingSymbol ( ), std::move ( decodedData ));
    }

    /**
      * Decode a BWT-encoded string and remove terminating symbol, reusing precomputed auxiliary data.
      * This implementation uses the W decoding array. Required data type is W_FORWARD_DECODE.
      *
      * @tparam SymbolType
      * @param str the input BWT-encoded string
      * @param data precomputed auxiliary data
      * @return the decoded string with removed terminating symbol
      */
    template < class SymbolType >
    static string::LinearString < SymbolType >
    decodeUsingWArray ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str,
                        const indexes::stringology::BWTAuxiliaryData < SymbolType > & data ) {
        if ( ! data.isDataTypePresent ( indexes::stringology::BWTAuxiliaryDataType::W_FORWARD_DECODE ))
            throw exception::CommonException ( "Decoding BWT string requires W array to be present." );

        measurements::start ( "BWTDecodeNaive using W array", measurements::Type::ALGORITHM );
        ext::vector < SymbolType > decodedData;
        unsigned i = data.getTerminatingSymbolIndex ( );
        for ( size_t j = 0; j < str.size ( ); j ++ ) {
            i = data.getWForwardDecode ( )[ i ];
            decodedData.push_back ( str.getContent ( )[ i ] );
        }
        measurements::end();

        return removeTerminatingSymbol ( str.getAlphabet ( ), str.getTerminatingSymbol ( ), std::move ( decodedData ));
    }
};

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


} /* namespace transform */

} /* namespace stringology */

