#include "vacuum_telemetry.h"

#include <stddef.h>

static bool valid_adc(uint16_t raw, uint16_t vref_mv)
{
    return raw <= VACUUM_ADC_MAX_COUNT && vref_mv >= 1000u && vref_mv <= 3600u;
}

bool vacuum_adc_count_to_mv(uint16_t raw, uint16_t vref_mv, uint32_t *millivolts)
{
    if (!valid_adc(raw, vref_mv) || millivolts == NULL) {
        return false;
    }

    *millivolts = ((uint64_t)raw * vref_mv + (VACUUM_ADC_MAX_COUNT / 2u)) /
                  VACUUM_ADC_MAX_COUNT;
    return true;
}

bool vacuum_vdda_from_vrefint(uint16_t vrefint_raw, uint16_t vrefint_cal_raw,
                              uint32_t *vdda_mv)
{
    uint64_t estimate_mv;

    if (vdda_mv == NULL || vrefint_raw == 0u || vrefint_raw > VACUUM_ADC_MAX_COUNT ||
        vrefint_cal_raw == 0u || vrefint_cal_raw > VACUUM_ADC_MAX_COUNT) {
        return false;
    }

    /* ST factory calibration stores VREFINT ADC data at VREF+ = 3.0 V.
     * RM0454 defines VREF+ = 3000 mV * CAL / current VREFINT_DATA. */
    estimate_mv = ((uint64_t)VACUUM_VREFINT_CAL_VREF_MV * vrefint_cal_raw +
                   (vrefint_raw / 2u)) / vrefint_raw;
    if (estimate_mv < VACUUM_VDDA_MIN_MV || estimate_mv > VACUUM_VDDA_MAX_MV) {
        return false;
    }

    *vdda_mv = (uint32_t)estimate_mv;
    return true;
}

bool vacuum_vbus_count_to_mv(uint16_t raw, uint16_t vref_mv, uint32_t *vbus_mv)
{
    uint32_t adc_mv;
    const uint64_t divider_total_ohm =
        (uint64_t)VACUUM_VBUS_DIVIDER_TOP_OHM + VACUUM_VBUS_DIVIDER_BOTTOM_OHM;

    if (vbus_mv == NULL || !vacuum_adc_count_to_mv(raw, vref_mv, &adc_mv)) {
        return false;
    }

    *vbus_mv = (uint32_t)(((uint64_t)adc_mv * divider_total_ohm +
                           (VACUUM_VBUS_DIVIDER_BOTTOM_OHM / 2u)) /
                          VACUUM_VBUS_DIVIDER_BOTTOM_OHM);
    return true;
}

bool vacuum_current_count_to_ma(uint16_t raw, uint16_t vref_mv, uint32_t *current_ma)
{
    uint32_t adc_mv;
    const uint64_t gain_shunt_uohm =
        (uint64_t)VACUUM_CURRENT_AMP_GAIN * VACUUM_SHUNT_UOHM;

    if (current_ma == NULL || !vacuum_adc_count_to_mv(raw, vref_mv, &adc_mv)) {
        return false;
    }

    /* INA180 output is converted using the candidate 20 V/V and 10 mΩ values.
     * This is a nominal estimate until VDDA, gain, shunt and offset are calibrated. */
    *current_ma = (uint32_t)(((uint64_t)adc_mv * 1000000u + (gain_shunt_uohm / 2u)) /
                             gain_shunt_uohm);
    return true;
}

bool vacuum_timer_autoreload(uint32_t timer_clock_hz, uint32_t requested_hz,
                             uint16_t *auto_reload)
{
    uint64_t period_counts;

    if (auto_reload == NULL || timer_clock_hz == 0u || requested_hz == 0u ||
        requested_hz > timer_clock_hz) {
        return false;
    }

    period_counts = ((uint64_t)timer_clock_hz + (requested_hz / 2u)) / requested_hz;
    if (period_counts < 2u || period_counts > 65536u) {
        return false;
    }

    *auto_reload = (uint16_t)(period_counts - 1u);
    return true;
}
