#include "tools/bpmcalculator.h"

#include <cmath>
#include <numeric>
#include <iostream>

BPMCalculator::BPMCalculator()
{
    reset();
}

void BPMCalculator::reset()
{
    _calculating = false;
    _deltas.clear();
    _previous_click_offset = 0;
    _first_click_offset = 0;
    _need_recalculate = true;
}

void BPMCalculator::start()
{
    reset();

    _calculating = true;
}

void BPMCalculator::stop()
{
    _calculating = false;
}

bool BPMCalculator::calculating() const
{
    return _calculating;
}

void BPMCalculator::click(const kku::microsec &offset)
{
    if (!_calculating)
        return;

    _need_recalculate = true;

    if (_previous_click_offset == 0)
    {
        _previous_click_offset = offset;
        _first_click_offset = offset;
        return;
    }

    const kku::microsec delta = offset - _previous_click_offset;
    _deltas.emplace_back(delta);

    _previous_click_offset = offset;
}

const beat_utils::BeatInfo& BPMCalculator::fetchApproximatedInfo()
{
    if (!_need_recalculate)
        return _approximated_info;

    _need_recalculate = false;

    bool hasEnoughDeltas = _deltas.size() >= 8;

    _approximated_info = (!hasEnoughDeltas)
                       ?  beat_utils::BeatInfo{}
                       :  beat_utils::calculateBeatRateInfo(_deltas);

    return _approximated_info;
}

kku::microsec BPMCalculator::getStartingOffset() const
{
    return _first_click_offset;
}

void BPMCalculator::setStartingOffset(kku::microsec offset)
{
    _first_click_offset = offset;
}

void BPMCalculator::moveStartingOffsetBy(kku::microsec shift)
{
    _first_click_offset += shift;
}

kku::microsec BPMCalculator::fetchTimeUntilNextBeat(const kku::microsec& offset)
{
    const kku::microsec actual_offset = offset - getStartingOffset();

    return actual_offset % fetchApproximatedInfo().interval;
}
