#include "bingo_sim_storge.h"

#include <iostream>

using namespace bingo;

SimStorage::SimStorage(int fp_size, int mt_size, int inc_size)
    : _fingerprint_table(BingoAddr::bingo_null), _inc_size(inc_size), _mt_size(mt_size), _fp_size(fp_size)
{
    _inc_buffer.allocate(_inc_size * _fp_size);
    _inc_id_buffer.allocate(_inc_size * _fp_size);
}

BingoAddr SimStorage::create(BingoPtr<SimStorage>& ptr, int fp_size, int mt_size, int inc_size)
{
    ptr.allocate();
    new (ptr.ptr()) SimStorage(fp_size, mt_size, inc_size);

    return (BingoAddr)ptr;
}

void SimStorage::load(BingoPtr<SimStorage>& ptr, BingoAddr offset)
{
    ptr = BingoPtr<SimStorage>(offset);
}

void SimStorage::add(const byte* fingerprint, int id)
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
    {
        memcpy(_inc_buffer.ptr() + (_inc_fp_count * _fp_size), fingerprint, _fp_size);
        _inc_id_buffer[_inc_fp_count] = id;

        _inc_fp_count++;

        if (_inc_fp_count == _inc_size)
        {
            FingerprintTable::create(_fingerprint_table, _fp_size, _mt_size);
            for (int i = 0; i < _inc_fp_count; i++)
                _fingerprint_table->add(_inc_buffer.ptr() + (i * _fp_size), _inc_id_buffer[i]);

            _inc_fp_count = 0;
        }
    }
    else
    {
        _fingerprint_table->add(fingerprint, id);
    }
}

void SimStorage::optimize()
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        return;

    _fingerprint_table->optimize();
}

int SimStorage::getCellCount() const
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    return _fingerprint_table->getCellCount();
}

int SimStorage::getCellSize(int cell_idx) const
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    return _fingerprint_table->getCellSize(cell_idx);
}

void SimStorage::getCellsInterval(const byte* query, SimCoef& sim_coef, double min_coef, int& min_cell, int& max_cell)
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    _fingerprint_table->getCellsInterval(query, sim_coef, min_coef, min_cell, max_cell);
}

int SimStorage::firstFitCell(int query_bit_count, int min_cell, int max_cell) const
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    return _fingerprint_table->firstFitCell(query_bit_count, min_cell, max_cell);
}

int SimStorage::nextFitCell(int query_bit_count, int first_fit_cell, int min_cell, int max_cell, int idx) const
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    return _fingerprint_table->nextFitCell(query_bit_count, first_fit_cell, min_cell, max_cell, idx);
}

int SimStorage::getSimilar(const byte* query, SimCoef& sim_coef, double min_coef, Array<SimResult>& sim_fp_indices, int cell_idx, int cont_idx)
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        throw Exception("SimStorage: fingerptint table wasn't built");

    return _fingerprint_table->getSimilar(query, sim_coef, min_coef, sim_fp_indices, cell_idx, cont_idx);
}

bool SimStorage::isSmallBase()
{
    if ((BingoAddr)_fingerprint_table == BingoAddr::bingo_null)
        return true;
    return false;
}

int SimStorage::getIncSimilar(const byte* query, SimCoef& sim_coef, double min_coef, Array<SimResult>& sim_fp_indices)
{
    for (int i = 0; i < _inc_fp_count; i++)
    {
        double coef = sim_coef.calcCoef(_inc_buffer.ptr() + (i * _fp_size), query, -1, -1);
        if (coef < min_coef)
            continue;
        size_t id = _inc_id_buffer[i];

        sim_fp_indices.push(SimResult(id, coef));
    }

    return sim_fp_indices.size();
}

SimStorage::~SimStorage()
{
}