/*
 * FMIndex.h
 *
 *  Created on: 26. 02. 2021
 *      Author: Martin Prokopic
 */

#pragma once

#include <common/DefaultSymbolType.h>
#include <string/LinearStringTerminatingSymbolBWT.h>
#include <indexes/common/IndexesNormalize.h>

namespace indexes {

namespace stringology {

/**
 * Simple in-memory uncompressed FM-index.
 *
 * @tparam SymbolType
 */
template < class SymbolType = DefaultSymbolType >
class SimpleFMIndex {
    /** the BWT string */
    string::LinearStringTerminatingSymbolBWT < SymbolType > m_string;

    /** length of a superbucket, must be a multiple of m_bucketSize */
    unsigned m_superBucketSize;
    /** length of a bucket */
    unsigned m_bucketSize;
    /** m_superBucketOcc[i][c] contains number of occurrences of symbol c in superbuckets [0..(i/m_superBucketSize)-1]
     *  (symbols [0..((i/m_superBucketSize)-1)*m_superBucketSize] in m_string) */
    ext::vector < ext::map < SymbolType, unsigned > > m_superBucketOcc;
    /** m_bucketOcc[i][c] contains number of occurences of symbol c in buckets from the start of superbucket until bucket (i/m_bucketSize)-1 */
    ext::vector < ext::map < SymbolType, unsigned > > m_bucketOcc;

    ext::map < SymbolType, unsigned > m_mOccPrefixSum;
    ext::map < SymbolType, unsigned > m_kOcc;

    SymbolType m_markedSymbol;
    ext::vector < unsigned > m_sparseFirstToText;

public:
    explicit SimpleFMIndex ( string::LinearStringTerminatingSymbolBWT < SymbolType > str,
                             unsigned superBucketSize, unsigned bucketSize,
                             ext::vector < ext::map < SymbolType, unsigned > > superBucketOcc,
                             ext::vector < ext::map < SymbolType, unsigned > > bucketOcc,
                             ext::map < SymbolType, unsigned > mOccPrefixSum,
                             ext::map < SymbolType, unsigned > kOcc,
                             SymbolType markedSymbol,
                             ext::vector < unsigned > sparseFirstToText );

    const string::LinearStringTerminatingSymbolBWT < SymbolType > & getString ( ) const & {
        return m_string;
    }

    string::LinearStringTerminatingSymbolBWT < SymbolType > && getString ( ) && {
        return std::move ( m_string );
    }

    unsigned getSuperBucketSize ( ) const {
        return m_superBucketSize;
    }

    unsigned getBucketSize ( ) const {
        return m_bucketSize;
    }

    const ext::vector < ext::map < SymbolType, unsigned > > & getSuperBucketOcc ( ) const & {
        return m_superBucketOcc;
    }

    ext::vector < ext::map < SymbolType, unsigned > > && getSuperBucketOcc ( ) && {
        return std::move ( m_superBucketOcc );
    }

    const ext::vector < ext::map < SymbolType, unsigned > > & getBucketOcc ( ) const & {
        return m_bucketOcc;
    }

    ext::vector < ext::map < SymbolType, unsigned > > && getBucketOcc ( ) && {
        return std::move ( m_bucketOcc );
    }

    const ext::map < SymbolType, unsigned > & getMOccPrefixSum ( ) const & {
        return m_mOccPrefixSum;
    }

    ext::map < SymbolType, unsigned > && getMOccPrefixSum ( ) && {
        return std::move ( m_mOccPrefixSum );
    }

    const ext::map < SymbolType, unsigned > & getKOcc ( ) const & {
        return m_kOcc;
    }

    ext::map < SymbolType, unsigned > && getKOcc ( ) && {
        return std::move ( m_kOcc );
    }

    const SymbolType & getMarkedSymbol ( ) const {
        return m_markedSymbol;
    }

    const ext::vector < unsigned > & getSparseFirstToText ( ) const {
        return m_sparseFirstToText;
    }

    friend std::ostream & operator << ( std::ostream & out, const SimpleFMIndex & instance ) {
        out << "(FMIndex";
        out << " String = " << instance.m_string;
        out << " SuperBucketSize = " << instance.m_superBucketSize;
        out << " BucketSize = " << instance.m_bucketSize;
        out << " SuperBucketOcc = " << instance.m_superBucketOcc;
        out << " BucketOcc = " << instance.m_bucketOcc;
        out << " MOccPrefixSum = " << instance.m_mOccPrefixSum;
        out << " KOcc = " << instance.m_kOcc;
        out << " MarkedSymbol = " << instance.m_markedSymbol;
        out << " SparseFirstToText = " << instance.m_sparseFirstToText;
        out << ")";
        return out;
    }

    auto operator <=> ( const SimpleFMIndex & other ) const = default;

    bool operator == ( const SimpleFMIndex & other ) const = default;

    explicit operator std::string ( ) const;
};

template < class SymbolType >
SimpleFMIndex < SymbolType >::SimpleFMIndex ( string::LinearStringTerminatingSymbolBWT < SymbolType > str,
                                              unsigned int superBucketSize, unsigned int bucketSize,
                                              ext::vector < ext::map < SymbolType, unsigned int>> superBucketOcc,
                                              ext::vector < ext::map < SymbolType, unsigned int>> bucketOcc,
                                              ext::map < SymbolType, unsigned int > mOccPrefixSum,
                                              ext::map < SymbolType, unsigned int > kOcc,
                                              SymbolType markedSymbol,
                                              ext::vector < unsigned int > sparseFirstToText ) :
        m_string ( std::move ( str )),
        m_superBucketSize ( superBucketSize ),
        m_bucketSize ( bucketSize ),
        m_superBucketOcc ( std::move ( superBucketOcc )),
        m_bucketOcc ( std::move ( bucketOcc )),
        m_mOccPrefixSum ( std::move ( mOccPrefixSum )),
        m_kOcc ( std::move ( kOcc )),
        m_markedSymbol ( std::move ( markedSymbol )),
        m_sparseFirstToText ( std::move ( sparseFirstToText )) {
}

template < class SymbolType >
SimpleFMIndex < SymbolType >::operator std::string ( ) const {
    std::stringstream ss;
    ss << *this;
    return ss.str ( );
}

} /* namespace stringology */

} /* namespace indexes */

namespace core {

/**
 * Helper for normalisation of types specified by templates used as internal datatypes of symbols.
 *
 * \returns new instance of the index with default template parameters or unmodified instance if the template parameters were already the default ones
 */
template < class SymbolType >
struct normalize < indexes::stringology::SimpleFMIndex < SymbolType > > {
private:
    static ext::vector < ext::map < DefaultSymbolType, unsigned > >
    normalizeBucketOcc ( ext::vector < ext::map < SymbolType, unsigned > > && occ ) {
        ext::vector < ext::map < DefaultSymbolType, unsigned > > res;
        for ( auto && bucket : ext::make_mover ( occ ))
            res.push_back ( indexes::IndexesNormalize::normalizeMap ( std::move ( bucket )));
        return res;
    }

public:
    static indexes::stringology::SimpleFMIndex < >
    eval ( indexes::stringology::SimpleFMIndex < SymbolType > && value ) {
        auto string = normalize < string::LinearStringTerminatingSymbolBWT < SymbolType > >::eval (
                std::move ( value ).getString ( ));
        auto superBucketOcc = normalizeBucketOcc ( std::move ( value ).getSuperBucketOcc ( ));
        auto bucketOcc = normalizeBucketOcc ( std::move ( value ).getBucketOcc ( ));
        auto mOccPrefixSum = indexes::IndexesNormalize::normalizeMap ( std::move ( value ).getMOccPrefixSum ( ));
        auto kOcc = indexes::IndexesNormalize::normalizeMap ( std::move ( value ).getKOcc ( ));
        auto markedSymbol = alphabet::SymbolNormalize::normalizeSymbol ( std::move ( value ).getMarkedSymbol ( ));

        return indexes::stringology::SimpleFMIndex < > ( string, value.getSuperBucketSize ( ), value.getBucketSize ( ),
                                                         superBucketOcc, bucketOcc, mOccPrefixSum, kOcc,
                                                         markedSymbol, value.getSparseFirstToText ( ));
    }
};

} /* namespace core */



