/*
 * Kneron Hardware Test: GPIO
 *
 * Copyright (C) 2023 Kneron, Inc. All rights reserved.
 */

#include "cmsis_os2.h"
#include "kdrv_pinmux.h"
#include "kdrv_gpio.h"
#include "kmdw_console.h"

// Define a structure to hold a pair of GPIO pins for loopback testing.
typedef struct {
    uint32_t pin_a_num;
    const char* pin_a_name;
    uint32_t pin_b_num;
    const char* pin_b_name;
} gpio_pair_t;

// Centralize all GPIO test pairs in one array.
static const gpio_pair_t gpio_test_pairs[] = {
    {GPIO_PIN_25, "P37", GPIO_PIN_26, "P36"},
    {GPIO_PIN_23, "P34", GPIO_PIN_4,  "P32"},
    {GPIO_PIN_21, "P27", GPIO_PIN_20, "P26"},
    {GPIO_PIN_27, "P35", GPIO_PIN_24, "P38"}
};
static const int num_pairs = sizeof(gpio_test_pairs) / sizeof(gpio_test_pairs[0]);

// Helper function to run a single test case.
static void run_gpio_test_case(uint32_t output_pin_num, const char* output_pin_name, uint32_t input_pin_num, const char* input_pin_name, bool output_val, uint32_t *pass_count, uint32_t *fail_count)
{
    kdrv_gpio_write_pin((kdrv_gpio_pin_t)output_pin_num, output_val);
    kmdw_printf("Set %-3s output to: %s, ", output_pin_name, output_val ? "High" : "Low");
    osDelay(10); // Short delay to ensure signal stability.

    uint32_t input_val = 0;

    kdrv_gpio_read_pin((kdrv_gpio_pin_t)input_pin_num, (bool *)&input_val);

    bool passed = ((bool)input_val == output_val);
    kmdw_printf("Read %-3s input as: %s -> %s\n", input_pin_name, input_val ? "High" : "Low", passed ? "PASSED" : "FAILED");

    if (passed) {
        (*pass_count)++;
    } else {
        (*fail_count)++;
    }
}

int hw_test_gpio_run(void)
{
    // 1. Configure Pinmux
    // Note: The comments for GPIO numbers seem to be copy-paste errors and may not be accurate.
    // It's recommended to verify them against the hardware documentation.
    kdrv_pinmux_config(KDRV_PIN_SD_DAT_1, PIN_MODE_3, PIN_PULL_NONE, PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_SD_DAT_2, PIN_MODE_3, PIN_PULL_UP,   PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_SD_CMD,   PIN_MODE_3, PIN_PULL_NONE, PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_15, PIN_MODE_3, PIN_PULL_UP, PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_10, PIN_MODE_3, PIN_PULL_NONE, PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_LC_DATA_9, PIN_MODE_3, PIN_PULL_UP,   PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_SD_DAT_3, PIN_MODE_3, PIN_PULL_NONE, PIN_DRIVING_8MA);
    kdrv_pinmux_config(KDRV_PIN_SD_DAT_0, PIN_MODE_3, PIN_PULL_UP,   PIN_DRIVING_8MA);

    // 2. Initialize GPIO controller
    kdrv_gpio_initialize();

    kmdw_printf("\n");
    kmdw_printf("=========================================================\n");
    kmdw_printf("Please connect GPIO pairs for loopback test:\n");
    kmdw_printf("P37-P36, P34-P32, P27-P26, P35-P38\n");
    kmdw_printf("The test will start in 1 seconds...\n");
    kmdw_printf("=========================================================\n");
    osDelay(1000);

    uint32_t pass_count = 0;
    uint32_t fail_count = 0;

    // 3. First pass: Pin A is output, Pin B is input
    kmdw_printf("--- Test Pass 1: A->B ---\n");
    for (int i = 0; i < num_pairs; i++) {
        kdrv_gpio_set_attribute((kdrv_gpio_pin_t)gpio_test_pairs[i].pin_a_num, GPIO_DIR_OUTPUT);
        kdrv_gpio_set_attribute((kdrv_gpio_pin_t)gpio_test_pairs[i].pin_b_num, GPIO_DIR_INPUT);
    }

    for (int i = 0; i < num_pairs; i++) {
        const gpio_pair_t* pair = &gpio_test_pairs[i];
        run_gpio_test_case(pair->pin_a_num, pair->pin_a_name, pair->pin_b_num, pair->pin_b_name, false, &pass_count, &fail_count); // Test Low
        run_gpio_test_case(pair->pin_a_num, pair->pin_a_name, pair->pin_b_num, pair->pin_b_name, true, &pass_count, &fail_count);  // Test High
        kmdw_printf("\n");
        osDelay(50); 
    }

    kmdw_printf("=========================================================\n");
    kmdw_printf("Now swapping input/output roles and testing again...\n");
    kmdw_printf("=========================================================\n");
    osDelay(1000);

    // 4. Second pass: Pin B is output, Pin A is input (roles swapped)
    kmdw_printf("--- Test Pass 2: B->A ---\n");
    for (int i = 0; i < num_pairs; i++) {
        kdrv_gpio_set_attribute((kdrv_gpio_pin_t)gpio_test_pairs[i].pin_a_num, GPIO_DIR_INPUT);
        kdrv_gpio_set_attribute((kdrv_gpio_pin_t)gpio_test_pairs[i].pin_b_num, GPIO_DIR_OUTPUT);
    }

    for (int i = 0; i < num_pairs; i++) {
        const gpio_pair_t* pair = &gpio_test_pairs[i];
        run_gpio_test_case(pair->pin_b_num, pair->pin_b_name, pair->pin_a_num, pair->pin_a_name, false, &pass_count, &fail_count); // Test Low
        run_gpio_test_case(pair->pin_b_num, pair->pin_b_name, pair->pin_a_num, pair->pin_a_name, true, &pass_count, &fail_count);  // Test High
        kmdw_printf("\n");
        osDelay(50);
    }

    kmdw_printf("=========================================================\n");
    kmdw_printf("GPIO Test Summary:\n");
    kmdw_printf("  PASSED: %d\n", pass_count);
    kmdw_printf("  FAILED: %d\n", fail_count);
    kmdw_printf("=========================================================\n");
    kmdw_printf("Test finished. Returning to main menu...\n");
    osDelay(1000);
    if (fail_count == 0) {
        return 0;
    } else {
        return -1;
    }
}
