#include "cmsis_os2.h"
#include "kdrv_gpio.h"
#include <stdlib.h>
#include "kdrv_uart.h"
#include "base.h"
#include "kmdw_console.h"
#include "kdrv_pinmux.h"
#include "kdrv_pwm.h"
#include "kdrv_adc.h"
#include "kdrv_timer.h"

/*************************************************************************************************/
/*                                    Macro-Definitions                                          */
/*************************************************************************************************/
/*
 * !! Please modify the following definitions according to your hardware design !!
 * You must use a jumper to actually connect the pins corresponding to PWM_PIN and ADC_PIN.
 */

// 1. Select the physical PIN to use (refer to kdrv_pinmux.h for pin names)
#define PWM_PIN                 (KDRV_PIN_LC_DATA_12) // FIXME: Please replace with your actual PWM output pin

// 2. Select PIN_MODE for the required function (refer to kdrv_pinmux.h or chip datasheet)
#define PWM_PIN_MODE            (PIN_MODE_2)  // FIXME: Please replace with the PWM mode corresponding to your selected pin

// 3. Find the PWM channel and ADC channel corresponding to the selected PIN
#define PWM_CHANNEL             (PWMTIMER6)   // FIXME: Please replace with the corresponding PWM timer channel
#define ADC_CHANNEL             (3)           // FIXME: Please replace with the corresponding ADC channel

// 4. Test parameters
#define PWM_FREQUENCY_HZ        (1000)        // PWM Frequency (Hz)
#define PWM_POLARITY            (PWM_POLARITY_INVERSED) // 0 for active-high, 1 for active-low

#define ADC_MAX_VALUE           (1023)        // ADC max value (e.g., 4095 for 12-bit, 1023 for 10-bit)
#define TEST_VOLTAGE_TOLERANCE  (50)          // Allowed ADC reading tolerance (e.g., allow 50 error within 0-4095 range)

/*************************************************************************************************/
/*                                    Function Declarations                                      */
/*************************************************************************************************/

/**
 * @brief  Initialize hardware components, including Pinmux, UART, ADC, and PWM.
 */
static void hw_test_initialize(void)
{
    // Initialize UART for debug output
    // kmdw_console_init() is usually called at system startup, so it is not repeated here
    kmdw_printf("\n[ADC-PWM Loopback Test] Initializing...\n");

    // -- Key Step: Configure Pin Function --
    // Configure PWM pin
    kdrv_pinmux_config(PWM_PIN, PWM_PIN_MODE, PIN_PULL_NONE, PIN_DRIVING_8MA);
    kmdw_printf("Pinmux configured: PWM on pin %d (mode %d)\n", PWM_PIN, PWM_PIN_MODE);

    // Initialize ADC peripheral
    if (kdrv_adc_initialize() != KDRV_STATUS_OK) {
        kmdw_printf("Error: ADC peripheral initialization failed!\n");
        while(1);
    }
    kmdw_printf("ADC Initialized\n");

    kmdw_printf("Initialization complete. Please connect pin P29 to pin ADC3 with a jumper wire.\n");
    osDelay(3000); // Give user time to read message and connect wires
}

/**
 * @brief  Run PWM to ADC loopback test
 */
int hw_test_pwm_adc_run(void)
{
    hw_test_initialize();

    const uint32_t duty_cycles[] = {10, 40, 70, 90};
    const uint32_t num_steps = sizeof(duty_cycles) / sizeof(duty_cycles[0]);
    uint32_t pass_count = 0, adc_value = 0;

    // Calculate period based on expected frequency (Unit: 10ns)
    // Period (10ns) = (1 / Frequency Hz) / (1 / System Clock Hz) / 10ns
    // = 1,000,000,000 ns / Frequency Hz / 10ns
    const uint32_t period_10ns = 100000000 / PWM_FREQUENCY_HZ;

    kmdw_printf("\n======== Starting PWM-ADC Loopback Test ========\n");

    for (int i = 0; i < num_steps; i++) {
        uint32_t duty_percentage = duty_cycles[i];

        // Calculate duty cycle (Unit: 10ns)
        uint32_t duty_10ns = (period_10ns * duty_percentage) / 100;

        // 1. Configure PWM output
        kdrv_pwm_config(PWM_CHANNEL, PWM_POLARITY, duty_10ns, period_10ns, 0);

        // Enable PWM channel
        kdrv_pwm_enable(PWM_CHANNEL);

        // Delay to wait for voltage stability
        osDelay(200);

        // 2. Read ADC value
        adc_value = kdrv_adc_read(ADC_CHANNEL);
        // 3. Compare with expected value
        // Note: 100% duty cycle might not produce ADC_MAX_VALUE, depending on whether Vref is the same. This is an approximation.
        uint32_t expected_value = (ADC_MAX_VALUE * duty_percentage) / 100;
        int32_t diff = abs((int)adc_value - (int)expected_value);

        kmdw_printf("Test [Duty %3u%%]: Expected ADC ~%4u, Got ADC = %4u, Diff = %3d ... ",
                   duty_percentage, expected_value, adc_value, diff);

        if (diff <= TEST_VOLTAGE_TOLERANCE) {
            kmdw_printf("PASS\n");
            pass_count++;
        } else {
            kmdw_printf("FAIL\n");
        }
        kdrv_pwm_disable(PWM_CHANNEL);
        osDelay(200);
    }

    kmdw_printf("======== Test Cycle Complete ========\n");
    if (pass_count == num_steps) {
        kmdw_printf("Result: ALL TESTS PASSED.\n");
        return 0;
    } else {
        kmdw_printf("Result: %u/%u tests FAILED. Check hardware connection/functionality.\n", num_steps - pass_count, num_steps);
        return -1;
    }
}
