// tempo.cpp — see tempo.h. Pure; standard library only.

#include "core/instrument/note/tempo.h"

#include <cmath>

namespace reasampler::instrument::note {
namespace {
constexpr double kSecondsPerMinute = 60.0;
}  // namespace

std::optional<Tempo> Tempo::fromBpm(double beatsPerMinute) {
    if (!std::isfinite(beatsPerMinute) || beatsPerMinute <= 0.0) return std::nullopt;
    // Guard by running the conversions, not by testing the 60/bpm reciprocal they start
    // from: that reciprocal stays finite for BPMs whose beatsToMs has already overflowed,
    // because the conversions scale it by up to kMaxConvertibleMagnitude. Both directions
    // are checked — one overflows at an absurdly slow tempo, the other at an absurdly fast
    // one. Calling them here is what keeps the guard from drifting away from what they do.
    const Tempo candidate(beatsPerMinute);
    if (!std::isfinite(candidate.beatsToMs(kMaxConvertibleMagnitude))) return std::nullopt;
    if (!std::isfinite(candidate.msToBeats(kMaxConvertibleMagnitude))) return std::nullopt;
    return candidate;
}

double Tempo::secondsPerBeat() const { return kSecondsPerMinute / bpm_; }

double Tempo::beatsToSeconds(double beats) const { return beats * secondsPerBeat(); }

double Tempo::secondsToBeats(double seconds) const { return seconds / secondsPerBeat(); }

double Tempo::beatsToMs(double beats) const { return secondsToMs(beatsToSeconds(beats)); }

double Tempo::msToBeats(double ms) const { return secondsToBeats(msToSeconds(ms)); }

}  // namespace reasampler::instrument::note
