/**
 * @file test_cross_vendor_compute.c
 * @brief Test suite for cross-vendor GPU compute
 * 
 * This test verifies that a simple compute workload can be executed
 * across multiple GPUs from different vendors (if available).
 * 
 * Run with: ./test_cross_vendor_compute --verbose
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <mvgal/mvgal.h>
#include <mvgal/mvgal_gpu.h>

/* Test configuration */
#define TEST_BUFFER_SIZE (64 * 1024 * 1024)  /* 64 MB */
#define TEST_ITERATIONS 100

/* Test kernel: simple vector addition */
static const char *test_kernel_source = 
    "__kernel void vec_add(__global const float *a, "
    "__global const float *b, __global float *c) { "
    "int gid = get_global_id(0); "
    "c[gid] = a[gid] + b[gid]; }";

/* Test helpers */
static int tests_run = 0;
static int tests_passed = 0;
static int tests_failed = 0;

#define TEST(name) static void test_##name(void)
#define RUN_TEST(name) do { \
    printf("Running %s... ", #name); \
    tests_run++; \
    test_##name(); \
    printf("PASSED\n"); \
    tests_passed++; \
} while(0)

#define ASSERT(cond) do { \
    if (!(cond)) { \
        printf("FAILED at line %d: %s\n", __LINE__, #cond); \
        tests_failed++; \
        return; \
    } \
} while(0)

/* ============================================================================
 * Test 1: Device Detection
 * ============================================================================ */

TEST(device_detection)
{
    mvgal_error_t err = mvgal_init(0);
    ASSERT(err == MVGAL_SUCCESS);
    
    mvgal_gpu_info_t *gpus = NULL;
    uint32_t count = 0;
    
    err = mvgal_enumerate_gpus(&gpus, &count);
    ASSERT(err == MVGAL_SUCCESS);
    
    printf("\nDetected %u GPU(s):\n", count);
    for (uint32_t i = 0; i < count; i++) {
        printf("  GPU %u: vendor=0x%x device=0x%x\n", 
               i, gpus[i].vendor_id, gpus[i].device_id);
    }
    
    /* We need at least 1 GPU for meaningful tests */
    ASSERT(count >= 1);
    
    mvgal_shutdown();
}

/* ============================================================================
 * Test 2: Single GPU Compute (if available)
 * ============================================================================ */

TEST(single_gpu_compute)
{
    mvgal_error_t err = mvgal_init(0);
    ASSERT(err == MVGAL_SUCCESS);
    
    mvgal_gpu_info_t *gpus = NULL;
    uint32_t count = 0;
    
    err = mvgal_enumerate_gpus(&gpus, &count);
    if (err != MVGAL_SUCCESS || count < 1) {
        printf("SKIPPED (no GPUs available)\n");
        mvgal_shutdown();
        return;
    }
    
    printf("\nTesting compute on GPU 0...\n");
    
    /* Create a context */
    mvgal_context_t ctx = NULL;
    err = mvgal_context_create(&ctx);
    ASSERT(err == MVGAL_SUCCESS);
    
    /* Allocate buffers */
    uint64_t input_buf, output_buf;
    err = mvgal_allocate_memory(TEST_BUFFER_SIZE, MVGAL_MEM_HOST_VALID | MVGAL_MEM_GPU_VALID, &input_buf);
    ASSERT(err == MVGAL_SUCCESS);
    
    err = mvgal_allocate_memory(TEST_BUFFER_SIZE, MVGAL_MEM_HOST_VALID | MVGAL_MEM_GPU_VALID, &output_buf);
    ASSERT(err == MVGAL_SUCCESS);
    
    /* TODO: Submit compute workload */
    /* For now, just verify memory allocation worked */
    
    mvgal_context_destroy(ctx);
    mvgal_shutdown();
}

/* ============================================================================
 * Test 3: Multi-GPU Detection
 * ============================================================================ */

TEST(multi_gpu_detection)
{
    mvgal_error_t err = mvgal_init(0);
    ASSERT(err == MVGAL_SUCCESS);
    
    mvgal_gpu_info_t *gpus = NULL;
    uint32_t count = 0;
    
    err = mvgal_enumerate_gpus(&gpus, &count);
    ASSERT(err == MVGAL_SUCCESS);
    
    if (count < 2) {
        printf("SKIPPED (need 2+ GPUs, found %u)\n", count);
        mvgal_shutdown();
        return;
    }
    
    printf("\nTesting multi-GPU configuration...\n");
    
    /* Check vendor diversity */
    uint32_t vendors = 0;
    for (uint32_t i = 0; i < count; i++) {
        switch (gpus[i].vendor_id) {
            case 0x1002: vendors |= 1 << 0; break;  /* AMD */
            case 0x10DE: vendors |= 1 << 1; break;  /* NVIDIA */
            case 0x8086: vendors |= 1 << 2; break;  /* Intel */
            case 0x1ED5: vendors |= 1 << 3; break;  /* Moore Threads */
        }
    }
    
    /* Count unique vendors */
    uint32_t vendor_count = __builtin_popcount(vendors);
    printf("Found %u unique vendor(s)\n", vendor_count);
    
    if (vendor_count >= 2) {
        printf("SUCCESS: Heterogeneous multi-GPU configuration detected!\n");
    } else {
        printf("INFO: Single-vendor configuration (vendor diversity test passed)\n");
    }
    
    mvgal_shutdown();
}

/* ============================================================================
 * Test 4: P2P Capability Detection
 * ============================================================================ */

TEST(p2p_capability)
{
    mvgal_error_t err = mvgal_init(0);
    ASSERT(err == MVGAL_SUCCESS);
    
    mvgal_gpu_info_t *gpus = NULL;
    uint32_t count = 0;
    
    err = mvgal_enumerate_gpus(&gpus, &count);
    if (err != MVGAL_SUCCESS || count < 2) {
        printf("SKIPPED\n");
        mvgal_shutdown();
        return;
    }
    
    printf("\nP2P capability matrix:\n");
    
    /* Note: In a real implementation, this would query the kernel module
     * for the P2P matrix. For now, we just print a placeholder. */
    for (uint32_t i = 0; i < count; i++) {
        for (uint32_t j = 0; j < count; j++) {
            printf("  GPU %u -> GPU %u: unknown\n", i, j);
        }
    }
    
    mvgal_shutdown();
}

/* ============================================================================
 * Main
 * ============================================================================ */

int main(int argc, char *argv[])
{
    int verbose = 0;
    
    if (argc > 1 && strcmp(argv[1], "--verbose") == 0) {
        verbose = 1;
    }
    
    printf("=== MVGAL Cross-Vendor Compute Test Suite ===\n\n");
    
    /* Run tests */
    RUN_TEST(device_detection);
    RUN_TEST(single_gpu_compute);
    RUN_TEST(multi_gpu_detection);
    RUN_TEST(p2p_capability);
    
    /* Print results */
    printf("\n=== Test Results ===\n");
    printf("Tests run:    %d\n", tests_run);
    printf("Tests passed: %d\n", tests_passed);
    printf("Tests failed: %d\n", tests_failed);
    
    /* Print summary */
    if (tests_failed == 0) {
        printf("\nSUCCESS: All tests passed!\n");
    } else {
        printf("\nFAILURE: %d test(s) failed\n", tests_failed);
    }
    
    return tests_failed > 0 ? 1 : 0;
}