#include "aricoder.h"

#include "bitops.h"

#include <algorithm>
#include <cstdlib>
#include <functional>
#include <limits>

template <std::uint8_t bit>
void ArithmeticBitWriter::write_bit()
{
    // add bit at last position
    curr_byte_ = (curr_byte_ << 1) | bit;
    // increment bit position
    curr_bit_++;

    // write bit if done
    if (curr_bit_ == 8)
    {
        data_.emplace_back(curr_byte_);
        curr_bit_ = 0;
    }
}

void ArithmeticBitWriter::write_n_zero_bits(std::size_t n)
{
    if (n + curr_bit_ >= 8)
    {
        auto remainingBits = 8 - curr_bit_;
        n -= remainingBits;
        curr_byte_ <<= remainingBits;
        data_.emplace_back(curr_byte_);
        curr_bit_ = 0;
    }

    while (n >= 8)
    {
        data_.emplace_back(0);
        n -= 8;
    }

    curr_byte_ <<= n;
    curr_bit_ += n;
}

void ArithmeticBitWriter::write_n_one_bits(std::size_t n)
{
    constexpr std::uint8_t all_ones = std::numeric_limits<std::uint8_t>::max();
    if (n + curr_bit_ >= 8)
    {
        auto remainingBits = 8 - curr_bit_;
        n -= remainingBits;
        curr_byte_ <<= remainingBits;
        curr_byte_ |= all_ones >> (8 - remainingBits);
        data_.emplace_back(curr_byte_);
        curr_bit_ = 0;
    }

    while (n >= 8)
    {
        data_.emplace_back(all_ones);
        n -= 8;
    }

    curr_byte_ = (curr_byte_ << n) | (all_ones >> (8 - n));
    curr_bit_ += n;
}

void ArithmeticBitWriter::pad()
{
    while (curr_bit_ > 0)
    {
        write_bit<0>();
    }
}

std::vector<std::uint8_t> ArithmeticBitWriter::get_data() const
{
    return data_;
}

ArithmeticDecoder::ArithmeticDecoder(Reader& reader) : reader_(reader)
{
    // code buffer has to be filled before starting decoding
    for (std::uint32_t i = 0; i < CODER_USE_BITS; i++)
    {
        ccode = (ccode << 1) | read_bit();
    }
}

ArithmeticEncoder::ArithmeticEncoder(Writer& writer) : writer_(writer) {}

ArithmeticEncoder::~ArithmeticEncoder()
{
    if (!finalized)
    {
        this->finalize();
    }
}

void ArithmeticEncoder::finalize()
{
    if (finalized)
    {
        return;
    }

    // due to clow < CODER_LIMIT050, and chigh >= CODER_LIMIT050
    // there are only two possible cases
    if (clow < CODER_LIMIT025)
    {
        bitwriter_->write_bit<0>();
        bitwriter_->write_bit<1>();
        bitwriter_->write_n_one_bits(nrbits);
        nrbits = 0;
    }
    else
    {
        // case b.), clow >= CODER_LIMIT025
        bitwriter_->write_bit<1>();
    }
    // done, zeroes are auto-read by the decoder

    bitwriter_->pad(); // Pad code with zeroes.
    writer_.write(bitwriter_->get_data());

    finalized = true;
}

/* -----------------------------------------------
    arithmetic encoder function
    ----------------------------------------------- */

void ArithmeticEncoder::encode(symbol* s)
{
    // Make local copies of clow_ and chigh_ for cache performance:
    uint32_t clow_local = clow;
    uint32_t chigh_local = chigh;
    // update steps, low count, high count
    cstep = (chigh_local - clow_local + 1) / s->scale;
    chigh_local = clow_local + (cstep * s->high_count) - 1;
    clow_local = clow_local + (cstep * s->low_count);

    // e3 scaling is performed for speed and to avoid underflows
    // if both, low and high are either in the lower half or in the higher half
    // one bit can be safely shifted out
    while (clow_local >= CODER_LIMIT050  || chigh_local < CODER_LIMIT050)
    {
        if (chigh_local < CODER_LIMIT050)       // this means both, high and low are below, and 0 can be safely shifted out
        {
            // write 0 bit
            bitwriter_->write_bit<0>();
            // shift out remaing e3 bits
            bitwriter_->write_n_one_bits(nrbits);
            nrbits = 0;
        }
        else   // if the first wasn't the case, it's clow >= CODER_LIMIT050
        {
            // write 1 bit
            bitwriter_->write_bit<1>();
            clow_local &= CODER_LIMIT050 - 1;
            chigh_local &= CODER_LIMIT050 - 1;
            // shift out remaing e3 bits
            bitwriter_->write_n_zero_bits(nrbits);
            nrbits = 0;
        }
        clow_local <<= 1;
        chigh_local <<= 1;
        chigh_local++;
    }

    // e3 scaling, to make sure that theres enough space between low and high
    while ((clow_local >= CODER_LIMIT025) && (chigh_local < CODER_LIMIT075))
    {
        nrbits++;
        clow_local &= CODER_LIMIT025 - 1;
        chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
        // clow  -= CODER_LIMIT025;
        // chigh -= CODER_LIMIT025;
        clow_local <<= 1;
        chigh_local <<= 1;
        chigh_local++;
    }

    clow = clow_local;
    chigh = chigh_local;
}


/* -----------------------------------------------
    arithmetic decoder get count function
    ----------------------------------------------- */

unsigned int ArithmeticDecoder::decode_count(symbol* s)
{
    // update cstep, which is needed to remove the symbol from the stream later
    cstep = ((chigh - clow) + 1) / s->scale;

    // return counts, needed to decode the symbol from the statistical model
    return (ccode - clow) / cstep;
}

/* -----------------------------------------------
    arithmetic decoder function
    ----------------------------------------------- */

void ArithmeticDecoder::decode(symbol* s)
{
    // no actual decoding takes place, as this has to happen in the statistical model
    // the symbol has to be removed from the stream, though

    // alread have steps updated from decoder_count
    // update low count and high count
    uint32_t ccode_local = ccode;
    uint32_t clow_local = clow;
    uint32_t chigh_local = clow_local + (cstep * s->high_count) - 1;
    clow_local = clow_local + (cstep * s->low_count);

    // e3 scaling is performed for speed and to avoid underflows
    // if both, low and high are either in the lower half or in the higher half
    // one bit can be safely shifted out
    while ((clow_local >= CODER_LIMIT050) || (chigh_local < CODER_LIMIT050))
    {
        if (clow_local >= CODER_LIMIT050)
        {
            clow_local &= CODER_LIMIT050 - 1;
            chigh_local &= CODER_LIMIT050 - 1;
            ccode_local &= CODER_LIMIT050 - 1;
        } // if the first wasn't the case, it's chigh < CODER_LIMIT050
        clow_local <<= 1;
        chigh_local <<= 1;
        chigh_local++;
        ccode_local <<= 1;
        ccode_local |= read_bit();
    }

    // e3 scaling, to make sure that theres enough space between low and high
    while ((clow_local >= CODER_LIMIT025) && (chigh_local < CODER_LIMIT075))
    {
        clow_local &= CODER_LIMIT025 - 1;
        chigh_local ^= CODER_LIMIT025 + CODER_LIMIT050;
        // clow  -= CODER_LIMIT025;
        // chigh -= CODER_LIMIT025;
        ccode_local -= CODER_LIMIT025;
        clow_local <<= 1;
        chigh_local <<= 1;
        chigh_local++;
        ccode_local <<= 1;
        ccode_local |= read_bit();
    }
    chigh = chigh_local;
    clow = clow_local;
    ccode = ccode_local;
}

/* -----------------------------------------------
    bit reader function
    ----------------------------------------------- */

unsigned char ArithmeticDecoder::read_bit()
{
    // read in new byte if needed
    if (cbit == 0)
    {
        if (!reader_.read_byte(&bbyte))  // read next byte if available
        {
            bbyte = 0;    // if no more data is left in the stream
        }
        cbit = 8;
    }

    // decrement current bit position
    cbit--;
    // return bit at cbit position
    return BITN(bbyte, cbit);
}


/* -----------------------------------------------
    universal statistical model for arithmetic coding

    boundaries of this model:
    max_s (maximum symbol) -> 1 <= max_s <= 1024 (???)
    max_c (maximum context) -> 1 <= max_c <= 1024 (???)
    max_o (maximum order) -> -1 <= max_o <= 4
    c_lim (maximum count) -> 2 <= c_lim <= 4096 (???)
    WARNING: this can be memory intensive, so don't overdo it
    max_s == 256; max_c == 256; max_o == 4 would be way too much
    ----------------------------------------------- */

model_s::model_s(int max_s, int max_c, int max_o, int c_lim) :
    // Copy settings into the model:
    max_symbol(max_s),
    max_context(max_c),
    max_order(max_o + 1),
    max_count(c_lim),

    current_order(max_o + 1),
    sb0_count(max_s),

    totals(max_s + 2),
    scoreboard(new bool[max_s]),
    contexts(max_o + 3)
{
    std::fill(scoreboard, scoreboard + max_symbol, false);

    // set up null table
    table_s* null_table = new table_s;
    null_table->counts = std::vector<uint16_t>(max_symbol, uint16_t(1));  // Set all probabilities to 1.

    // set up internal counts
    null_table->max_count = 1;
    null_table->max_symbol = max_symbol;

    // set up start table
    table_s* start_table = new table_s;
    start_table->links = std::vector<table_s*>(max_context);

    // integrate tables into contexts
    contexts[ 0 ] = null_table;
    contexts[ 1 ] = start_table;

    // build initial 'normal' tables
    for (int i = 2; i <= max_order; i++)
    {
        // set up current order table
        contexts[i] = new table_s;
        // build forward links
        if (i < max_order)
        {
            contexts[i]->links = std::vector<table_s*>(max_context);
        }
        contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
    }
}


/* -----------------------------------------------
    model class destructor - recursive cleanup of memory is done here
    ----------------------------------------------- */

model_s::~model_s()
{
    // clean up each 'normal' table
    delete contexts[1];

    // clean up null table
    delete contexts[0];

    // free everything else
    delete[] scoreboard;
}


/* -----------------------------------------------
    Updates statistics for a specific symbol / resets to highest order.
    Use -1 if you just want to reset without updating statistics.
    ----------------------------------------------- */
void model_s::update_model(int symbol)
{
    // only contexts, that were actually used to encode
    // the symbol get its count updated
    if (symbol >= 0)
    {
        for (int local_order = (current_order < 1) ? 1 : current_order;
                local_order <= max_order; local_order++)
        {
            table_s* context = contexts[ local_order ];
            auto& count = context->counts[symbol];
            // update count for specific symbol & scale
            count++;
            // store side information for totalize_table
            context->max_count = std::max(count, context->max_count);
            context->max_symbol = std::max(uint16_t(symbol + 1), context->max_symbol);
            // if count for that symbol have gone above the maximum count
            // the table has to be resized (scale factor 2)
            if (count == max_count)
            {
                context->rescale_table();
            }
        }
    }

    // reset scoreboard and current order
    current_order = max_order;
    std::fill(scoreboard, scoreboard + max_symbol, false);
    sb0_count = max_symbol;
}


/* -----------------------------------------------
    shift in one context (max no of contexts is max_c)
    ----------------------------------------------- */

void model_s::shift_context(int c)
{
    // shifting is not possible if max_order is below 1
    // or context index is negative
    if ((max_order < 2) || (c < 0))
    {
        return;
    }

    // shift each orders' context
    for (int i = max_order; i > 1; i--)
    {
        // this is the new current order context
        table_s* context = contexts[ i - 1 ]->links[ c ];

        // check if context exists, build if needed
        if (context == nullptr)
        {
            // reserve memory for next table_s
            context = new table_s;
            // finished here if this is a max order context
            if (i < max_order)
            {
                // build links to higher order tables otherwise
                context->links.resize(max_context);
            }
            // put context to its right place
            contexts[ i - 1 ]->links[ c ] = context;
        }

        // switch context
        contexts[ i ] = context;
    }
}


/* -----------------------------------------------
    Flushes the entire model by calling rescale_table on all contexts.
    ----------------------------------------------- */

void model_s::flush_model()
{
    contexts[1]->recursive_flush();
}


/* -----------------------------------------------
    Excludes every symbol above c.
    ----------------------------------------------- */

void model_s::exclude_symbols(int c)
{
    // exclusions are back to normal after update_model is used

    for (c = c + 1; c < max_symbol; c++)
    {
        if (!scoreboard[ c ])
        {
            scoreboard[ c ] = true;
            sb0_count--;
        }
    }
}


/* -----------------------------------------------
    converts an int to a symbol, needed only when encoding
    ----------------------------------------------- */

int model_s::convert_int_to_symbol(int c, symbol* s)
{
    // search the symbol c in the current context table_s,
    // return scale, low- and high counts

    // totalize table for the current context
    table_s* context = contexts[ current_order ];
    totalize_table(context);

    // finding the scale is easy
    s->scale = totals[ 0 ];

    // check if that symbol exists in the current table. send escape otherwise
    if (context->counts[ c ] > 0)
    {
        // return high and low count for the current symbol
        s->low_count  = totals[ c + 2 ];
        s->high_count = totals[ c + 1 ];
        return 0;
    }

    // return high and low count for the escape symbol
    s->low_count  = totals[ 1 ];
    s->high_count = totals[ 0 ];
    current_order--;
    return 1;
}


/* -----------------------------------------------
    returns the current context scale needed only when decoding
    ----------------------------------------------- */

void model_s::get_symbol_scale(symbol* s)
{
    // getting the scale is easy: totalize the table_s, use accumulated count -> done
    totalize_table(contexts[ current_order ]);
    s->scale = totals[ 0 ];
}


/* -----------------------------------------------
    converts a count to an int, called after get_symbol_scale
    ----------------------------------------------- */

int model_s::convert_symbol_to_int(uint32_t count, symbol* s)
{
    // seek the symbol that matches the count,
    // also, set low- and high count for the symbol - it has to be removed from the stream


    // go through the totals table, search the symbol that matches the count
    int c;
    for (c = 1; c < int(totals.size()); c++)
    {
        if (count >= totals[c])
        {
            break;
        }
    }
    // set up the current symbol
    s->low_count = totals[c]; // It is guaranteed that there exists such a symbol.
    s->high_count = totals[c - 1]; // This is guaranteed to not go out of bounds since the search started at index 1 of totals.
    // send escape if escape symbol encountered
    if (c == 1)
    {
        current_order--;
        return ESCAPE_SYMBOL;
    }

    // return symbol value
    return c - 2 ; // Since c is not one and is a positive number, this will be nonnegative.
}


/* -----------------------------------------------
    totals are calculated by accumulating counts in the current table_s
    ----------------------------------------------- */

void model_s::totalize_table(table_s* context)
{
    // update exclusion is used, so this has to be done each time
    // escape probability calculation also takes place here

    // accumulated counts must never exceed CODER_MAXSCALE
    // as CODER_MAXSCALE is big enough, though, (2^29), this shouldn't happen and is not checked

    const auto& counts = context->counts;

    // check counts
    if (!counts.empty())    // if counts are already set
    {
        // locally store current fill/symbol count
        int local_symb = sb0_count;

        // set the last symbol of the totals to zero
        int i = context->max_symbol - 1;
        totals[i + 2] = 0;

        // (re)set current total
        uint32_t curr_total = 0;

        // go reverse though the whole counts table and accumulate counts
        // leave space at the beginning of the table for the escape symbol
        for (; i >= 0; i--)
        {
            // only count probability if the current symbol is not 'scoreboard - excluded'
            if (!scoreboard[i])
            {
                uint16_t curr_count = counts[i];
                if (curr_count > 0)
                {
                    // add counts for the current symbol
                    curr_total += curr_count;
                    // exclude symbol from scoreboard
                    scoreboard[i] = true;
                    sb0_count--;
                }
            }
            totals[i + 1] = curr_total;
        }
        // here the escape calculation needs to take place
        uint32_t esc_prob;
        if (local_symb == sb0_count)
        {
            esc_prob = 1;
        }
        else if (sb0_count == 0)
        {
            esc_prob = 0;
        }
        else
        {
            // esc_prob = 1;
            esc_prob  =  sb0_count * (local_symb - sb0_count);
            esc_prob /= (local_symb * context->max_count);
            esc_prob++;
        }
        // include escape probability in totals table
        totals[ 0 ] = totals[ 1 ] + esc_prob;
    }
    else     // if counts are not already set
    {
        // setup counts for current table
        context->counts.resize(max_symbol);
        // set totals table -> only escape probability included
        totals[ 0 ] = 1;
        totals[ 1 ] = 0;
    }
}

/* -----------------------------------------------
    special version of model_s for binary coding

    boundaries of this model:
    ... (maximum symbol) -> 2 (0 or 1 )
    max_c (maximum context) -> 1 <= max_c <= 1024 (???)
    max_o (maximum order) -> -1 <= max_o <= 4
    ----------------------------------------------- */

model_b::model_b(int max_c, int max_o, int c_lim) :
    // Copy settings into the model:
    max_context(max_c),
    max_order(max_o + 1),
    max_count(c_lim),

    contexts(max_o + 3)
{
    // set up null table
    table* null_table = new table;
    null_table->counts = std::vector<uint16_t>(2, uint16_t(1));
    null_table->scale = uint32_t(2);

    // set up start table
    table* start_table = new table;
    start_table->links = std::vector<table*>(max_context);

    // integrate tables into contexts
    contexts[ 0 ] = null_table;
    contexts[ 1 ] = start_table;

    // build initial 'normal' tables
    for (int i = 2; i <= max_order; i++)
    {
        // set up current order table
        contexts[i] = new table;
        // build forward links
        if (i < max_order)
        {
            contexts[i]->links = std::vector<table*>(max_context);
        }
        contexts[ i - 1 ]->links[ 0 ] = contexts[ i ];
    }
}


/* -----------------------------------------------
    model class destructor - recursive cleanup of memory is done here
    ----------------------------------------------- */

model_b::~model_b()
{
    // clean up each 'normal' table
    delete contexts[1];

    // clean up null table
    delete contexts[0];
}


/* -----------------------------------------------
    updates statistics for a specific symbol / resets to highest order
    ----------------------------------------------- */

void model_b::update_model(int symbol)
{
    // use -1 if you just want to reset without updating statistics

    table* context = contexts[ max_order ];

    // only contexts, that were actually used to encode
    // the symbol get their counts updated
    if ((symbol >= 0) && (max_order >= 0))
    {
        // update count for specific symbol & scale
        context->counts[ symbol ]++;
        context->scale++;
        // if counts for that symbol have gone above the maximum count
        // the table has to be resized (scale factor 2)
        if (context->counts[ symbol ] >= max_count)
        {
            context->rescale_table();
        }
    }
}


/* -----------------------------------------------
    shift in one context (max no of contexts is max_c)
    ----------------------------------------------- */

void model_b::shift_context(int c)
{
    // shifting is not possible if max_order is below 1
    // or context index is negative
    if ((max_order < 2) || (c < 0))
    {
        return;
    }

    // shift each orders' context
    for (int i = max_order; i > 1; i--)
    {
        // this is the new current order context
        table* context = contexts[ i - 1 ]->links[ c ];

        // check if context exists, build if needed
        if (context == nullptr)
        {
            // reserve memory for next table
            context = new table;
            // finished here if this is a max order context
            if (i < max_order)
            {
                // build links to higher order tables otherwise
                context->links.resize(max_context);
            }
            // put context to its right place
            contexts[ i - 1 ]->links[ c ] = context;
        }

        // switch context
        contexts[ i ] = context;
    }
}


/* -----------------------------------------------
    Flushes the entire model by calling rescale_table on all contexts.
    ----------------------------------------------- */

void model_b::flush_model()
{
    contexts[1]->recursive_flush();
}


/* -----------------------------------------------
    converts an int to a symbol, needed only when encoding
    ----------------------------------------------- */

int model_b::convert_int_to_symbol(int c, symbol* s)
{
    table* context = contexts[ max_order ];

    // check if counts are available
    context->check_counts();

    // finding the scale is easy
    s->scale = context->scale;

    // return high and low count for current symbol
    if (c == 0)     // if 0 is to be encoded
    {
        s->low_count  = uint32_t(0);
        s->high_count = context->counts[ 0 ];
    }
    else   // if 1 is to be encoded
    {
        s->low_count  = context->counts[ 0 ];
        s->high_count = context->scale;
    }

    return 1;
}


/* -----------------------------------------------
    returns the current context scale needed only when decoding
    ----------------------------------------------- */

void model_b::get_symbol_scale(symbol* s)
{
    table* context = contexts[ max_order ];

    // check if counts are available
    context->check_counts();

    // getting the scale is easy
    s->scale = context->scale;
}


/* -----------------------------------------------
    converts a count to an int, called after get_symbol_scale
    ----------------------------------------------- */

int model_b::convert_symbol_to_int(uint32_t count, symbol* s)
{
    table* context = contexts[ max_order ];
    auto counts0 = context->counts[ 0 ];

    // set up the current symbol
    if (count < counts0)
    {
        s->low_count  = uint32_t(0);
        s->high_count = counts0;
        return 0;
    }
    else
    {
        s->low_count  = counts0;
        s->high_count = s->scale;
        return 1;
    }
}
