#pragma once

#include <numeric>
#include <type_traits>
#include <vector>
#include <cmath>
#include <iostream>

#include "core/time.h"

namespace kku
{

template<typename Grade, typename = std::enable_if_t<std::is_enum<Grade>::value>>
class PrecisionEvaluator
{
public:
    PrecisionEvaluator(const std::vector<microsec>& intervals, microsec offset) :
        _offset(offset),
        _intervals(intervals)
    {
        _start_handling_offset = _offset - intervals.back();
        _end_handling_offset = _offset + intervals.back();
    }

    inline microsec offset() const noexcept
    {
        return _offset;
    }

    inline bool isActive(microsec music_play_offset) const noexcept
    {
        return music_play_offset > _start_handling_offset
            && music_play_offset < _end_handling_offset;
    }

    Grade calculatePrecision(microsec odds) const
    {
        microsec shift_from_perfect = std::abs(odds - offset());

        std::cout << "Shift " << ((odds > _offset) ? "late: " : "early: ")
                  << shift_from_perfect << "\n";

        std::size_t raw_grade;
        for (raw_grade = 0; raw_grade < _intervals.size(); ++raw_grade)
        {
            if (shift_from_perfect <= _intervals.at(raw_grade))
                break;
        }

        return static_cast<Grade>(raw_grade);
    }

private:
    microsec _offset;
    microsec _start_handling_offset;
    microsec _end_handling_offset;

    /* Amount of values in enum instanced as GradeS
     * represents capacity of _intervals.
     * So, for each V value in GradeS enum, _intervals[V]
     * should return time shift from V - 1.
     * V0 is PERFECT SCORE and the last V represents the worst
     * grades which is death of note by expiration */

    const std::vector<microsec> _intervals;
};

}
