/*
 * BWTNaive.h
 *
 *  Created on: 13. 02. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <alib/algorithm>

#include <string/LinearStringTerminatingSymbolBWT.h>
#include <alphabet/MinimalEndSymbol.h>
#include <alib/measure>

namespace stringology {

namespace transform {

class BWTNaive {
private:
    /** Compare two same-length strings using SymbolCompare. */
    template < class SymbolType >
    static bool stringCompare ( const ext::vector < SymbolType > & a, const ext::vector < SymbolType > & b );

    /** Append a minimal terminating symbol at the end of a LinearString. */
    template < class SymbolType >
    static string::LinearStringTerminatingSymbolBWT < SymbolType >
    appendTerminatingSymbol ( const string::LinearString < SymbolType > & str );

public:
    /**
     * Append a minimal terminating symbol to a LinearString and construct BWT of it.
     *
     * @tparam SymbolType
     * @param str the input string
     * @return the BWT-encoded input string with a minimal terminating symbol appended
     */
    template < class SymbolType >
    static string::LinearStringTerminatingSymbolBWT < SymbolType >
    construct ( const string::LinearString < SymbolType > & str ) {
        return construct ( appendTerminatingSymbol ( str ));
    }

    /**
     * Construct BWT of a linear string with minimal terminating symbol.
     *
     * @tparam SymbolType
     * @param str the input string with minimal terminating symbol at the end
     * @return the BWT-encoded input string
     */
    template < class SymbolType >
    static string::LinearStringTerminatingSymbolBWT < SymbolType >
    construct ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str );

};

template < class SymbolType >
string::LinearStringTerminatingSymbolBWT < SymbolType >
BWTNaive::construct ( const string::LinearStringTerminatingSymbolBWT < SymbolType > & str ) {
    const size_t size = str.size ( );

    measurements::start("BWTNaive", measurements::Type::ALGORITHM);

    // 1. generate all cyclic shifts
    ext::vector < ext::vector < SymbolType > > shifts ( size );
    for ( size_t i = 0; i < size; i ++ )
        for ( size_t j = 0; j < size; j ++ )
            shifts[ i ].push_back ( str.getContent ( )[ ( i + j ) % size ] );

    // 2. sort 'em
    std::sort ( shifts.begin ( ), shifts.end ( ), stringCompare < SymbolType > );

    // 3. take last characters
    ext::vector < SymbolType > bwtData;
    for ( size_t i = 0; i < size; i ++ )
        bwtData.push_back ( shifts[ i ][ size - 1 ] );

    measurements::end();

    return string::LinearStringTerminatingSymbolBWT < SymbolType > ( str.getAlphabet ( ), str.getTerminatingSymbol ( ),
                                                                     bwtData );
}

template < class SymbolType >
string::LinearStringTerminatingSymbolBWT < SymbolType >
BWTNaive::appendTerminatingSymbol ( const string::LinearString < SymbolType > & str ) {
    const SymbolType terminatingSymbol = alphabet::MinimalEndSymbol::instance < SymbolType > ( );
    ext::set < SymbolType > newAlphabet = str.getAlphabet ( );
    ext::vector < SymbolType > newContent = str.getContent ( );
    newAlphabet.insert ( terminatingSymbol );
    newContent.push_back ( terminatingSymbol );

    return string::LinearStringTerminatingSymbolBWT ( newAlphabet, terminatingSymbol, newContent );
}

template < class SymbolType >
bool BWTNaive::stringCompare ( const ext::vector < SymbolType > & a, const ext::vector < SymbolType > & b ) {
    alphabet::SymbolCompare < SymbolType > comp;

    for ( size_t i = 0; i < a.size ( ) && i < b.size ( ); i ++ ) {
        if ( comp ( a[ i ], b[ i ] )) // a[i] < b[i]
            return true;
        if ( comp ( b[ i ], a[ i ] ))
            return false;
    }

    return false;
}

} /* namespace transform */

} /* namespace stringology */

