/*
 * BigGrinRTX · AMOURANTHRTX · SPV + compute RTX · X11 IN · HW GL
 * Two lanes only: field = BGF measure · free = SPV (never fold)
 * Linear: field = BGF on BGS · free rides measure · never writes field.
 * no Vulkan · OpenGL compute on NVIDIA · one dispatch
 *
 * Build:  ./Build/x rtx
 * Run:    ./Build/x rtx
 */
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
#include <X11/Xlib.h>

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifndef GL_SHADER_STORAGE_BUFFER
#define GL_SHADER_STORAGE_BUFFER 0x90D2
#endif
#ifndef GL_SHADER_STORAGE_BARRIER_BIT
#define GL_SHADER_STORAGE_BARRIER_BIT 0x00002000
#endif
#ifndef GL_COMPUTE_SHADER
#define GL_COMPUTE_SHADER 0x91B9
#endif

static const char *COMP_SRC =
    "#version 430\n"
    "/* AMOURANTHRTX · sharp · not frosted · free SDF · SPV never fold */\n"
    "layout(local_size_x = 16, local_size_y = 16) in;\n"
    "layout(std430, binding = 0) buffer Field { int field[]; };\n"
    "layout(std430, binding = 1) buffer Free  { int free_spv[]; };\n"
    "layout(std430, binding = 2) readonly buffer Params {\n"
    "    int width;\n"
    "    int height;\n"
    "    int seed;\n"
    "    int _pad;\n"
    "};\n"
    "int bgf(int a, int b) { return (a - b) | 1; }\n"
    "int spv(int a, int b, int c) { return (a ^ b ^ c) | 1; }\n"
    "void main() {\n"
    "    int x = int(gl_GlobalInvocationID.x);\n"
    "    int y = int(gl_GlobalInvocationID.y);\n"
    "    if (x >= width || y >= height) return;\n"
    "    int i = y * width + x;\n"
    "    int cx = width / 2;\n"
    "    int cy = height / 2;\n"
    "    int dx = x - cx;\n"
    "    int dy = y - cy;\n"
    "    int ax = dx < 0 ? -dx : dx;\n"
    "    int ay = dy < 0 ? -dy : dy;\n"
    "    int cheb = ax > ay ? ax : ay;\n"
    "    int rad = min(width, height) / 4;\n"
    "    /* rtx: eax=field  r8=free · separate · no fold */\n"
    "    field[i]    = bgf(cheb, rad);\n"
    "    free_spv[i] = spv(x, y, seed);\n"
    "}\n";

static void die(const char *m) {
    fprintf(stderr, "rtx_glx: %s\n", m);
    exit(1);
}

static char *load_file(const char *path, long *out_len) {
    FILE *f = fopen(path, "rb");
    if (!f) return NULL;
    fseek(f, 0, SEEK_END);
    long n = ftell(f);
    fseek(f, 0, SEEK_SET);
    char *b = (char *)malloc((size_t)n + 1);
    if (!b) {
        fclose(f);
        return NULL;
    }
    if (fread(b, 1, (size_t)n, f) != (size_t)n) {
        free(b);
        fclose(f);
        return NULL;
    }
    b[n] = 0;
    fclose(f);
    if (out_len) *out_len = n;
    return b;
}

static GLuint compile_compute(const char *src) {
    GLuint sh = glCreateShader(GL_COMPUTE_SHADER);
    glShaderSource(sh, 1, &src, NULL);
    glCompileShader(sh);
    GLint ok = 0;
    glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
    if (!ok) {
        char log[4096];
        glGetShaderInfoLog(sh, sizeof log, NULL, log);
        fprintf(stderr, "compute compile:\n%s\n", log);
        die("shader compile failed");
    }
    GLuint prog = glCreateProgram();
    glAttachShader(prog, sh);
    glLinkProgram(prog);
    glGetProgramiv(prog, GL_LINK_STATUS, &ok);
    if (!ok) {
        char log[4096];
        glGetProgramInfoLog(prog, sizeof log, NULL, log);
        fprintf(stderr, "compute link:\n%s\n", log);
        die("program link failed");
    }
    glDeleteShader(sh);
    return prog;
}

static void write_ppm_field(const char *path, int w, int h, const int *field, const int *free_spv) {
    /* AMOURANTHRTX composite view: field → structure, free → chroma spark · never replace SDF */
    FILE *f = fopen(path, "wb");
    if (!f) die("ppm open");
    fprintf(f, "P6\n%d %d\n255\n", w, h);
    for (int i = 0; i < w * h; i++) {
        int d = field[i]; /* (cheb-rad)|1 signed-ish as int */
        /* map distance-ish to grey shell (sharp bands) */
        int a = d < 0 ? -d : d;
        int band = a & 255;
        int inside = (d < 0) ? 1 : 0;
        int fr = free_spv[i];
        int spark = fr & 31;
        unsigned char r, g, b;
        if (inside) {
            r = (unsigned char)(20 + (band % 40));
            g = (unsigned char)(40 + (band % 60));
            b = (unsigned char)(30 + (band % 40));
        } else if (band < 8) {
            /* shell edge sharp white-ish */
            r = g = b = 220;
        } else {
            r = (unsigned char)(10 + (band / 4 > 40 ? 40 : band / 4));
            g = (unsigned char)(12 + (band / 3 > 50 ? 50 : band / 3));
            b = (unsigned char)(11 + (band / 5 > 40 ? 40 : band / 5));
        }
        if (spark == 1) {
            r = (unsigned char)(r + 50 > 255 ? 255 : r + 50);
            g = (unsigned char)(g + 20 > 255 ? 255 : g + 20);
        }
        fputc(r, f);
        fputc(g, f);
        fputc(b, f);
    }
    fclose(f);
}

int main(int argc, char **argv) {
    int W = 512, H = 512, seed = 7;
    const char *comp_path = NULL;
    const char *out_ppm = NULL;
    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "--w") && i + 1 < argc) W = atoi(argv[++i]);
        else if (!strcmp(argv[i], "--h") && i + 1 < argc) H = atoi(argv[++i]);
        else if (!strcmp(argv[i], "--seed") && i + 1 < argc) seed = atoi(argv[++i]);
        else if (!strcmp(argv[i], "--comp") && i + 1 < argc) comp_path = argv[++i];
        else if (!strcmp(argv[i], "--out") && i + 1 < argc) out_ppm = argv[++i];
    }

    Display *dpy = XOpenDisplay(NULL);
    if (!dpy) die("XOpenDisplay (need X11 DISPLAY)");

    static int vis_attr[] = {GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None};
    XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), vis_attr);
    if (!vi) die("glXChooseVisual");

    Colormap cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
    XSetWindowAttributes swa;
    swa.colormap = cmap;
    swa.event_mask = ExposureMask;
    Window win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, 64, 64, 0, vi->depth,
                               InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
    XStoreName(dpy, win, "AMOURANTHRTX compute");
    XMapWindow(dpy, win);
    XFlush(dpy);

    GLXContext ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
    if (!ctx) die("glXCreateContext");
    if (!glXMakeCurrent(dpy, win, ctx)) die("glXMakeCurrent");

    const char *renderer = (const char *)glGetString(GL_RENDERER);
    const char *version = (const char *)glGetString(GL_VERSION);
    printf("AMOURANTHRTX · HW GL compute · X11 IN\n");
    printf("renderer: %s\n", renderer ? renderer : "?");
    printf("version:  %s\n", version ? version : "?");
    if (renderer && !strstr(renderer, "NVIDIA") && !strstr(renderer, "GeForce") &&
        !strstr(renderer, "RTX")) {
        fprintf(stderr, "warn: expected NVIDIA RTX path (hw_gl) · got: %s\n", renderer);
    }

    /* prefer file rtx.comp if loadable as GLSL after version poke; else embedded 430 */
    char *file_src = NULL;
    const char *src = COMP_SRC;
    if (comp_path) {
        long n = 0;
        file_src = load_file(comp_path, &n);
        if (file_src) {
            /* Vulkan #version 450 often works on NVIDIA GL; if not, embedded used after fail */
            src = file_src;
            printf("comp: %s\n", comp_path);
        }
    }

    GLuint prog = 0;
    /* try file then fall back */
    {
        GLuint sh = glCreateShader(GL_COMPUTE_SHADER);
        glShaderSource(sh, 1, &src, NULL);
        glCompileShader(sh);
        GLint ok = 0;
        glGetShaderiv(sh, GL_COMPILE_STATUS, &ok);
        if (!ok) {
            char log[2048];
            glGetShaderInfoLog(sh, sizeof log, NULL, log);
            fprintf(stderr, "file/comp compile fail, embedded 430:\n%s\n", log);
            glDeleteShader(sh);
            free(file_src);
            file_src = NULL;
            prog = compile_compute(COMP_SRC);
        } else {
            prog = glCreateProgram();
            glAttachShader(prog, sh);
            glLinkProgram(prog);
            glGetProgramiv(prog, GL_LINK_STATUS, &ok);
            if (!ok) {
                glDeleteProgram(prog);
                glDeleteShader(sh);
                free(file_src);
                prog = compile_compute(COMP_SRC);
            } else {
                glDeleteShader(sh);
            }
        }
    }

    size_t n_pix = (size_t)W * (size_t)H;
    size_t bytes = n_pix * sizeof(int);
    int params[4] = {W, H, seed, 0};

    GLuint ssbo[3];
    glGenBuffers(3, ssbo);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)bytes, NULL, GL_DYNAMIC_COPY);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, (GLsizeiptr)bytes, NULL, GL_DYNAMIC_COPY);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[2]);
    glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof params, params, GL_DYNAMIC_DRAW);

    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssbo[0]);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssbo[1]);
    glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssbo[2]);

    glUseProgram(prog);
    GLuint gx = (GLuint)((W + 15) / 16);
    GLuint gy = (GLuint)((H + 15) / 16);
    printf("dispatch: %ux%u groups · %dx%d · seed=%d · one shot\n", gx, gy, W, H, seed);
    glDispatchCompute(gx, gy, 1);
    glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);

    int *field = (int *)malloc(bytes);
    int *free_spv = (int *)malloc(bytes);
    if (!field || !free_spv) die("oom");
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[0]);
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)bytes, field);
    glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo[1]);
    glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, (GLsizeiptr)bytes, free_spv);

    /* plate */
    printf("field[0]=%d free[0]=%d · linear line · never fold\n", field[0], free_spv[0]);
    printf("field[mid]=%d free[mid]=%d\n", field[n_pix / 2], free_spv[n_pix / 2]);

    /*
     * NEVER-FOLD PROOF (embedded GLSL law)
     * ------------------------------------
     * Linear law is not a second ground. Free is a ride on BGF measure, not a twin root
     * lanes: field = BGF measure, free = SPV. Fold would be writing free
     * into field (or the reverse). We prove:
     *   1) every field[i] reconstructs as bgf(cheb,rad)  — measure only
     *   2) every free_spv[i] reconstructs as spv(x,y,seed) — free only
     *   3) free values never appear as field writes (lanes independent)
     * Coincidence field[i]==free[i] is NOT a fold. Assignment is.
     */
    {
        int cx = W / 2, cy = H / 2;
        int rad = (W < H ? W : H) / 4;
        int field_bad = 0, free_bad = 0, odd_bad = 0, coincide = 0;
        for (int y = 0; y < H; y++) {
            for (int x = 0; x < W; x++) {
                int i = y * W + x;
                int dx = x - cx, dy = y - cy;
                int ax = dx < 0 ? -dx : dx;
                int ay = dy < 0 ? -dy : dy;
                int cheb = ax > ay ? ax : ay;
                int expect_f = (cheb - rad) | 1;          /* bgf */
                int expect_r = (x ^ y ^ seed) | 1;        /* spv */
                if (field[i] != expect_f) field_bad++;
                if (free_spv[i] != expect_r) free_bad++;
                if ((field[i] & 1) == 0 || (free_spv[i] & 1) == 0) odd_bad++;
                if (field[i] == free_spv[i]) coincide++;
            }
        }
        printf("never_fold_proof:\n");
        printf("  field_matches_bgf  %s  (bad=%d / %zu)\n",
               field_bad == 0 ? "PASS" : "FAIL", field_bad, n_pix);
        printf("  free_matches_spv   %s  (bad=%d / %zu)\n",
               free_bad == 0 ? "PASS" : "FAIL", free_bad, n_pix);
        printf("  both_lanes_odd|1   %s  (bad=%d)\n",
               odd_bad == 0 ? "PASS" : "FAIL", odd_bad);
        printf("  coincide_ok_not_fold  n=%d  (value match ≠ assignment)\n", coincide);
        printf("  static_law  free never assigned into field · linear line only\n");
        if (field_bad || free_bad || odd_bad) {
            fprintf(stderr, "never_fold_proof FAILED\n");
            free(field);
            free(free_spv);
            free(file_src);
            return 2;
        }
        printf("  RESULT  PASS · never fold · GRIN free=1\n");
    }

    if (!out_ppm) out_ppm = "out/amouranth_rtx_compute.ppm";
    write_ppm_field(out_ppm, W, H, field, free_spv);
    printf("wrote %s\n", out_ppm);

    free(field);
    free(free_spv);
    free(file_src);
    glDeleteProgram(prog);
    glDeleteBuffers(3, ssbo);
    glXMakeCurrent(dpy, None, NULL);
    glXDestroyContext(dpy, ctx);
    XDestroyWindow(dpy, win);
    XCloseDisplay(dpy);
    printf("AMOURANTHRTX compute OK · linear line · never fold · remain IN X11\n");
    return 0;
}
