#include <stdio.h>

#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>

static int present_renderer_frame(SDL_Renderer *renderer, Uint8 r, Uint8 g, Uint8 b) {

    if (!SDL_SetRenderDrawColor(renderer, r, g, b, 255)) {
        printf("SDL_SetRenderDrawColor failed: %s\n", SDL_GetError());
        return -1;
    }

    if (!SDL_RenderClear(renderer)) {
        printf("SDL_RenderClear failed: %s\n", SDL_GetError());
        return -1;
    }

    if (!SDL_RenderPresent(renderer)) {
        printf("SDL_RenderPresent failed: %s\n", SDL_GetError());
        return -1;
    }

    SDL_Delay(250);
    return 0;
}

static int clear_swapchain_with_gpu(SDL_Window *window, SDL_GPUDevice *device,
        float r, float g, float b) {

    SDL_GPUCommandBuffer *cmd;
    SDL_GPUTexture *swapchain = NULL;
    SDL_GPUColorTargetInfo color_target;
    SDL_GPURenderPass *pass;
    Uint32 width = 0;
    Uint32 height = 0;

    cmd = SDL_AcquireGPUCommandBuffer(device);
    if (!cmd) {
        printf("SDL_AcquireGPUCommandBuffer failed: %s\n", SDL_GetError());
        return -1;
    }

    if (!SDL_WaitAndAcquireGPUSwapchainTexture(cmd, window, &swapchain,
                &width, &height)) {
        printf("SDL_WaitAndAcquireGPUSwapchainTexture failed: %s\n",
                SDL_GetError());
        SDL_CancelGPUCommandBuffer(cmd);
        return -1;
    }

    if (!swapchain) {
        printf("No swapchain texture acquired\n");
        SDL_CancelGPUCommandBuffer(cmd);
        return -1;
    }

    SDL_zero(color_target);
    color_target.texture = swapchain;
    color_target.clear_color.r = r;
    color_target.clear_color.g = g;
    color_target.clear_color.b = b;
    color_target.clear_color.a = 1.0f;
    color_target.load_op = SDL_GPU_LOADOP_CLEAR;
    color_target.store_op = SDL_GPU_STOREOP_STORE;

    pass = SDL_BeginGPURenderPass(cmd, &color_target, 1, NULL);
    if (!pass) {
        printf("SDL_BeginGPURenderPass failed: %s\n", SDL_GetError());
        SDL_CancelGPUCommandBuffer(cmd);
        return -1;
    }

    SDL_EndGPURenderPass(pass);

    if (!SDL_SubmitGPUCommandBuffer(cmd)) {
        printf("SDL_SubmitGPUCommandBuffer failed: %s\n", SDL_GetError());
        return -1;
    }

    printf("Direct GPU clear submitted for %ux%u swapchain\n", width, height);
    SDL_Delay(250);
    return 0;
}

static int run_variant(const char *label, SDL_WindowFlags window_flags) {

    SDL_Window *window = NULL;
    SDL_Renderer *renderer = NULL;
    SDL_PropertiesID renderer_props;
    SDL_GPUDevice *device;
    SDL_GPUShaderFormat formats;
    int status = 0;

    printf("\n=== Variant: %s ===\n", label);

    window = SDL_CreateWindow(label, 640, 480, window_flags);
    if (!window) {
        printf("SDL_CreateWindow failed: %s\n", SDL_GetError());
        return 1;
    }

    renderer = SDL_CreateRenderer(window, "gpu");
    if (!renderer) {
        printf("SDL_CreateRenderer(window, \"gpu\") failed: %s\n",
                SDL_GetError());
        SDL_DestroyWindow(window);
        SDL_Quit();
        return 1;
    }

    printf("Renderer name: %s\n", SDL_GetRendererName(renderer));

    renderer_props = SDL_GetRendererProperties(renderer);
    device = SDL_GetPointerProperty(renderer_props,
            SDL_PROP_RENDERER_GPU_DEVICE_POINTER, NULL);

    if (!device) {
        printf("Renderer did not expose SDL_PROP_RENDERER_GPU_DEVICE_POINTER\n");
        status = 1;
        goto done;
    }

    printf("Borrowed SDL_GPUDevice from renderer\n");
    printf("GPU driver: %s\n", SDL_GetGPUDeviceDriver(device));
    formats = SDL_GetGPUShaderFormats(device);
    printf("Shader formats: 0x%08x\n", formats);

    if (present_renderer_frame(renderer, 180, 32, 48)) {
        status = 1;
        goto done;
    }

    if (clear_swapchain_with_gpu(window, device, 0.10f, 0.35f, 0.80f)) {
        printf("Borrowed-device direct GPU clear failed; trying explicit window claim\n");
        if (!SDL_ClaimWindowForGPUDevice(device, window)) {
            printf("SDL_ClaimWindowForGPUDevice failed: %s\n", SDL_GetError());
            status = 1;
            goto done;
        }

        if (clear_swapchain_with_gpu(window, device, 0.10f, 0.35f, 0.80f)) {
            SDL_ReleaseWindowFromGPUDevice(device, window);
            status = 1;
            goto done;
        }

        SDL_ReleaseWindowFromGPUDevice(device, window);
    }

    if (present_renderer_frame(renderer, 36, 150, 72)) {
        status = 1;
        goto done;
    }

    printf("Renderer/GPU coexistence spike completed\n");

done:
    SDL_DestroyRenderer(renderer);
    SDL_DestroyWindow(window);
    return status;
}

int main(int argc, char **argv) {

    int status = 0;
    (void)argc;
    (void)argv;

    if (!SDL_Init(SDL_INIT_VIDEO)) {
        printf("SDL_Init failed: %s\n", SDL_GetError());
        return 1;
    }

    status |= run_variant("gpu renderer borrowed device", 0);
    status |= run_variant("gpu renderer borrowed device with SDL_WINDOW_OPENGL",
            SDL_WINDOW_OPENGL);

    SDL_Quit();
    return status ? 1 : 0;
}
