// param_format.cpp — see param_format.h.

#include "core/instrument/param/param_format.h"

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <limits>

namespace reasampler::instrument::param {

void formatPlain(UnitKind kind, double plain, char* buf, std::size_t len) {
    if (!buf || len == 0) return;
    switch (kind) {
        case UnitKind::Time:
            // Never switches to seconds, so the ceiling reads 10000 and not 10 — units is one
            // static string per parameter and cannot change with magnitude. Sub-10 ms keeps a
            // decimal so a short attack is not rounded to a bare "0".
            std::snprintf(buf, len, plain < 10.0 ? "%.1f" : "%.0f", plain);
            return;
        case UnitKind::Semitones:
            std::snprintf(buf, len, "%+.1f", plain);
            return;
        case UnitKind::PercentUnipolar:
        case UnitKind::PercentKeyTrack:
            std::snprintf(buf, len, "%.0f", plain);
            return;
        case UnitKind::PercentBipolar:
            std::snprintf(buf, len, "%+.0f", plain);
            return;
        case UnitKind::PercentRate:
            // One decimal, not integer percent: the snap grid is whole semitones and those do
            // not land on integer percent (+1 st = 105.946 %), so an integer display would print
            // a snapped position as a value the snap cannot produce.
            std::snprintf(buf, len, "%.1f", plain);
            return;
        case UnitKind::Decibels:
            if (!std::isfinite(plain)) { std::snprintf(buf, len, "-inf"); return; }
            std::snprintf(buf, len, "%+.1f", plain);
            return;
        case UnitKind::Hertz:
            // The "k" abbreviation is RETIRED: units is one static string per parameter, so a
            // magnitude-switching unit is not expressible, and keeping "12.8k" in the editor
            // alone would be exactly the host/editor divergence one formatter exists to forbid.
            std::snprintf(buf, len, "%.0f", plain);
            return;
        case UnitKind::Dimensionless:
            std::snprintf(buf, len, "%.2f", plain);
            return;
    }
    buf[0] = '\0';
}

void formatPlainFor(DeckParam deck, double plain, char* buf, std::size_t len) {
    formatPlain(unitKindFor(deck), plain, buf, len);
}

bool parsePlain(UnitKind kind, const char* text, double& plain) {
    if (!text) return false;
    if (kind == UnitKind::Decibels) {
        // The one non-numeric string any formatter emits, so the one the parser must recognise.
        for (const char* p = text; *p; ++p) {
            if (*p == 'i' && p[1] == 'n' && p[2] == 'f') {
                plain = -std::numeric_limits<double>::infinity();
                return true;
            }
        }
    }
    char* end = nullptr;
    const double value = std::strtod(text, &end);
    if (end == text) return false;
    plain = value;
    return true;
}

}  // namespace reasampler::instrument::param
