#include "../src/vacuum_telemetry.h"

#include <assert.h>
#include <stdint.h>
#include <stdio.h>

static void test_adc_scaling(void)
{
    uint32_t millivolts = UINT32_MAX;

    assert(vacuum_adc_count_to_mv(0u, 3300u, &millivolts));
    assert(millivolts == 0u);
    assert(vacuum_adc_count_to_mv(4095u, 3300u, &millivolts));
    assert(millivolts == 3300u);
    assert(vacuum_adc_count_to_mv(2048u, 3300u, &millivolts));
    assert(millivolts == 1650u);
}

static void test_factory_vrefint_vdda_estimate(void)
{
    uint32_t vdda_mv = UINT32_MAX;

    /* CAL=1650 was acquired at 3.0 V; raw=1500 corresponds to 3.3 V. */
    assert(vacuum_vdda_from_vrefint(1650u, 1650u, &vdda_mv));
    assert(vdda_mv == 3000u);
    assert(vacuum_vdda_from_vrefint(1500u, 1650u, &vdda_mv));
    assert(vdda_mv == 3300u);
    assert(vacuum_vdda_from_vrefint(1375u, 1650u, &vdda_mv));
    assert(vdda_mv == 3600u);
    assert(vacuum_vdda_from_vrefint(2475u, 1650u, &vdda_mv));
    assert(vdda_mv == 2000u);

    assert(!vacuum_vdda_from_vrefint(0u, 1650u, &vdda_mv));
    assert(!vacuum_vdda_from_vrefint(4096u, 1650u, &vdda_mv));
    assert(!vacuum_vdda_from_vrefint(1650u, 0u, &vdda_mv));
    assert(!vacuum_vdda_from_vrefint(1650u, 4096u, &vdda_mv));
    assert(!vacuum_vdda_from_vrefint(3000u, 1650u, &vdda_mv)); /* below 2.0 V */
    assert(!vacuum_vdda_from_vrefint(1200u, 1650u, &vdda_mv)); /* above 3.6 V */
    assert(!vacuum_vdda_from_vrefint(1500u, 1650u, NULL));
}

static void test_vbus_divider_estimate(void)
{
    uint32_t vbus_mv = 0u;

    assert(vacuum_vbus_count_to_mv(2160u, 3300u, &vbus_mv));
    assert(vbus_mv >= 11950u && vbus_mv <= 12050u);
    assert(vacuum_vbus_count_to_mv(4095u, 3300u, &vbus_mv));
    assert(vbus_mv >= 22700u && vbus_mv <= 22800u);
}

static void test_current_shunt_estimate(void)
{
    uint32_t current_ma = 0u;

    assert(vacuum_current_count_to_ma(248u, 3300u, &current_ma));
    assert(current_ma >= 990u && current_ma <= 1010u);
    assert(vacuum_current_count_to_ma(4095u, 3300u, &current_ma));
    assert(current_ma == 16500u);
}

static void test_supply_calibration_flows_into_limits(void)
{
    uint32_t vdda_mv = 0u;
    uint32_t vbus_mv = 0u;
    uint32_t current_ma = 0u;

    assert(vacuum_vdda_from_vrefint(1650u, 1650u, &vdda_mv));
    assert(vacuum_vbus_count_to_mv(2160u, (uint16_t)vdda_mv, &vbus_mv));
    assert(vbus_mv >= 10850u && vbus_mv <= 10950u);
    assert(vacuum_current_count_to_ma(248u, (uint16_t)vdda_mv, &current_ma));
    assert(current_ma >= 900u && current_ma <= 920u);
}

static void test_rejects_invalid_adc_inputs(void)
{
    uint32_t value = 0u;

    assert(!vacuum_adc_count_to_mv(4096u, 3300u, &value));
    assert(!vacuum_adc_count_to_mv(100u, 0u, &value));
    assert(!vacuum_adc_count_to_mv(100u, 3601u, &value));
    assert(!vacuum_adc_count_to_mv(100u, 3300u, NULL));
    assert(!vacuum_vbus_count_to_mv(100u, 3300u, NULL));
    assert(!vacuum_current_count_to_ma(100u, 3300u, NULL));
}

static void test_timer_period_calculation(void)
{
    uint16_t arr = UINT16_MAX;

    assert(vacuum_timer_autoreload(16000000u, 25000u, &arr));
    assert(arr == 639u);
    assert(vacuum_timer_autoreload(32000000u, 25000u, &arr));
    assert(arr == 1279u);
    assert(!vacuum_timer_autoreload(1000000u, 1000000u, &arr));
    assert(!vacuum_timer_autoreload(2000000u, 1u, &arr));
    assert(!vacuum_timer_autoreload(0u, 25000u, &arr));
    assert(!vacuum_timer_autoreload(16000000u, 0u, &arr));
    assert(!vacuum_timer_autoreload(16000000u, 25000u, NULL));
}

int main(void)
{
    test_adc_scaling();
    test_factory_vrefint_vdda_estimate();
    test_vbus_divider_estimate();
    test_current_shunt_estimate();
    test_supply_calibration_flows_into_limits();
    test_rejects_invalid_adc_inputs();
    test_timer_period_calculation();
    puts("PASS: factory VREFINT/VDDA scaling, divider, current and PWM arithmetic boundaries.");
    return 0;
}
