/*
 * KateRTX · ZERO2D · 2D zero-cost demo · native GL window · NO host browser
 * Detects Wayland · X11 · GL · RTX · opens own window · blits 2D field
 *
 *   gcc -O2 -o bin/krtx-zero2d src/krtx_zero2d.c -lGL -lGLX -lX11 -lm
 *   ./bin/krtx-zero2d              # window forever
 *   ./bin/krtx-zero2d --frames 90 --dump out/zero2d.ppm
 *   ./bin/krtx-zero2d --detect     # JSON detect only
 *
 * LAW: SPV home = KateRTX · free thrift · BGS→BGF→BGL→SDF · C IS LIE
 */
#define GL_GLEXT_PROTOTYPES
#include <GL/gl.h>
#include <GL/glext.h>
#include <GL/glx.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/keysym.h>

#include <dirent.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#define W 640
#define H 360

typedef struct {
  int wayland;
  int x11;
  int glx;
  int rtx;
  int nvidia;
  char session[32];
  char wayland_display[64];
  char x_display[64];
  char gl_vendor[128];
  char gl_renderer[192];
  char gl_version[96];
  char gpu_name[128];
  char path_chosen[32]; /* x11-glx | wayland-pending | none */
  char note[256];
} Detect;

static void json_escape(const char *s, char *o, size_t n) {
  size_t j = 0;
  if (!s) {
    o[0] = 0;
    return;
  }
  for (size_t i = 0; s[i] && j + 2 < n; i++) {
    char c = s[i];
    if (c == '"' || c == '\\') {
      if (j + 3 >= n) break;
      o[j++] = '\\';
      o[j++] = c;
    } else if ((unsigned char)c < 32) {
      o[j++] = ' ';
    } else {
      o[j++] = c;
    }
  }
  o[j] = 0;
}

static int file_exists(const char *p) { return access(p, F_OK) == 0; }

static void detect_fill(Detect *d) {
  memset(d, 0, sizeof *d);
  const char *wd = getenv("WAYLAND_DISPLAY");
  const char *xd = getenv("DISPLAY");
  const char *st = getenv("XDG_SESSION_TYPE");
  if (wd && wd[0]) {
    d->wayland = 1;
    snprintf(d->wayland_display, sizeof d->wayland_display, "%s", wd);
  }
  /* also probe runtime wayland socket */
  if (!d->wayland) {
    const char *rt = getenv("XDG_RUNTIME_DIR");
    if (rt) {
      char path[512];
      snprintf(path, sizeof path, "%s/wayland-0", rt);
      if (file_exists(path)) {
        d->wayland = 1;
        snprintf(d->wayland_display, sizeof d->wayland_display, "wayland-0");
      }
    }
  }
  if (xd && xd[0]) {
    d->x11 = 1;
    snprintf(d->x_display, sizeof d->x_display, "%s", xd);
  } else if (file_exists("/tmp/.X11-unix/X0")) {
    d->x11 = 1;
    snprintf(d->x_display, sizeof d->x_display, ":0");
  }
  if (st && st[0])
    snprintf(d->session, sizeof d->session, "%s", st);
  else if (d->wayland && !d->x11)
    snprintf(d->session, sizeof d->session, "wayland");
  else if (d->x11)
    snprintf(d->session, sizeof d->session, "x11");
  else
    snprintf(d->session, sizeof d->session, "unknown");

  /* nvidia / rtx probe without requiring GL context yet */
  FILE *f = popen("nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1", "r");
  if (f) {
    if (fgets(d->gpu_name, sizeof d->gpu_name, f)) {
      /* trim */
      size_t L = strlen(d->gpu_name);
      while (L && (d->gpu_name[L - 1] == '\n' || d->gpu_name[L - 1] == '\r'))
        d->gpu_name[--L] = 0;
      d->nvidia = 1;
      if (strstr(d->gpu_name, "RTX") || strstr(d->gpu_name, "rtx")) d->rtx = 1;
    }
    pclose(f);
  }
  if (!d->gpu_name[0]) {
    f = popen("lspci 2>/dev/null | grep -iE 'VGA|3D|NVIDIA|GeForce' | head -1", "r");
    if (f) {
      if (fgets(d->gpu_name, sizeof d->gpu_name, f)) {
        size_t L = strlen(d->gpu_name);
        while (L && (d->gpu_name[L - 1] == '\n' || d->gpu_name[L - 1] == '\r'))
          d->gpu_name[--L] = 0;
        if (strstr(d->gpu_name, "NVIDIA") || strstr(d->gpu_name, "GeForce")) d->nvidia = 1;
        if (strstr(d->gpu_name, "RTX")) d->rtx = 1;
      }
      pclose(f);
    }
  }

  /* path choice · native window without host OS chrome
   * prefer X11+GLX today (present); Wayland noted for EGL path */
  if (d->x11) {
    snprintf(d->path_chosen, sizeof d->path_chosen, "x11-glx");
    if (d->wayland)
      snprintf(d->note, sizeof d->note,
               "Wayland socket seen · demo uses X11/GLX (XWayland OK) · native ELF window");
    else
      snprintf(d->note, sizeof d->note, "X11+GLX native window · no host browser · KateRTX SPV home");
  } else if (d->wayland) {
    snprintf(d->path_chosen, sizeof d->path_chosen, "wayland-pending");
    snprintf(d->note, sizeof d->note,
             "Wayland only · zero2d wants XWayland or future EGL · install DISPLAY or weston");
  } else {
    snprintf(d->path_chosen, sizeof d->path_chosen, "none");
    snprintf(d->note, sizeof d->note, "no display · set DISPLAY=:0 or WAYLAND_DISPLAY");
  }
}

static void detect_print_json(const Detect *d) {
  char v[160], r[220], ver[120], g[160], n[300];
  json_escape(d->gl_vendor, v, sizeof v);
  json_escape(d->gl_renderer, r, sizeof r);
  json_escape(d->gl_version, ver, sizeof ver);
  json_escape(d->gpu_name, g, sizeof g);
  json_escape(d->note, n, sizeof n);
  printf("{\n");
  printf("  \"word\": \"KATE_DETECT\",\n");
  printf("  \"home\": \"KateRTX\",\n");
  printf("  \"session\": \"%s\",\n", d->session);
  printf("  \"wayland\": %s,\n", d->wayland ? "true" : "false");
  printf("  \"wayland_display\": \"%s\",\n", d->wayland_display);
  printf("  \"x11\": %s,\n", d->x11 ? "true" : "false");
  printf("  \"x_display\": \"%s\",\n", d->x_display);
  printf("  \"glx\": %s,\n", d->glx ? "true" : "false");
  printf("  \"gl_vendor\": \"%s\",\n", v);
  printf("  \"gl_renderer\": \"%s\",\n", r);
  printf("  \"gl_version\": \"%s\",\n", ver);
  printf("  \"nvidia\": %s,\n", d->nvidia ? "true" : "false");
  printf("  \"rtx\": %s,\n", d->rtx ? "true" : "false");
  printf("  \"gpu\": \"%s\",\n", g);
  printf("  \"path\": \"%s\",\n", d->path_chosen);
  printf("  \"note\": \"%s\",\n", n);
  printf("  \"law\": \"KateRTX SPV home · native window · no host browser · free thrift\"\n");
  printf("}\n");
}

/* cheap 2D fragment · plasma + SDF circle · zero-cost vs raymarch */
static const char *VS =
    "#version 330 core\n"
    "out vec2 uv;\n"
    "void main(){\n"
    "  vec2 p = vec2((gl_VertexID<<1)&2, gl_VertexID&2);\n"
    "  uv = p*0.5;\n"
    "  gl_Position = vec4(p*2.0-1.0,0,1);\n"
    "}\n";

static const char *FS =
    "#version 330 core\n"
    "in vec2 uv;\n"
    "out vec4 C;\n"
    "uniform float u_time;\n"
    "uniform float u_rtx;\n"
    "uniform float u_view; /* 0 front 1 side 2 over_shoulder */\n"
    "vec2 proj(vec3 w, float view, float t){\n"
    "  vec3 p=w;\n"
    "  if(view>1.5){ float yaw=0.72; float cy=cos(yaw),sy=sin(yaw);\n"
    "    float pitch=-0.38; float cp=cos(pitch),sp=sin(pitch);\n"
    "    float x1=w.x*cy+w.z*sy; float z1=-w.x*sy+w.z*cy;\n"
    "    p=vec3(x1-0.15, w.y*cp-z1*sp+0.2, w.y*sp+z1*cp+1.15); }\n"
    "  else if(view>0.5){ p=vec3(w.z, w.y, -w.x); }\n"
    "  float persp=1.0/(1.25+p.z*0.5);\n"
    "  return vec2(p.x,p.y)*persp;\n"
    "}\n"
    "void main(){\n"
    "  vec2 p = uv*2.0-1.0; p.x *= 640.0/360.0;\n"
    "  float t=u_time; float view=u_view;\n"
    "  float ring=1e9;\n"
    "  for(int r=0;r<3;r++){\n"
    "    float rr=0.28+float(r)*0.22+0.03*sin(t*1.4+float(r)*2.1);\n"
    "    for(int i=0;i<32;i++){\n"
    "      float a=float(i)/32.0*6.28318+t*(0.45+float(r)*0.12);\n"
    "      vec2 rp=proj(vec3(cos(a)*rr,-0.05+float(r)*0.02,sin(a)*rr),view,t);\n"
    "      ring=min(ring, length(p-rp)-(0.02+0.008*float(r))); }\n"
    "  }\n"
    "  for(int k=0;k<10;k++){\n"
    "    float aa=t*0.9+float(k)*0.52; float pr=0.7;\n"
    "    vec2 dp=proj(vec3(cos(aa)*pr,0.2*sin(t+float(k)),sin(aa)*pr),view,t);\n"
    "    ring=min(ring, length(p-dp)-0.015);\n"
    "  }\n"
    "  vec3 mint=vec3(0.02,0.95,0.63); vec3 cyan=vec3(0.18,0.89,0.90); vec3 pink=vec3(1.0,0.16,0.43);\n"
    "  vec3 col=mix(vec3(0.02,0.025,0.03), vec3(0.04,0.06,0.08), uv.y);\n"
    "  float glow=exp(-max(ring,0.0)*18.0);\n"
    "  col+=vec3(0.05,0.12,0.14)*glow;\n"
    "  col=mix(col, cyan, smoothstep(0.05,0.0,abs(ring))*0.85);\n"
    "  col=mix(col, mint, smoothstep(0.018,0.0,abs(ring)));\n"
    "  if(u_rtx>0.5) col=mix(col,mint,0.05);\n"
    "  if(uv.y<0.04) col=mix(col,pink,0.7);\n"
    "  if(uv.y>0.96) col=mix(col,cyan,0.5);\n"
    "  C=vec4(col,1.0);\n"
    "}\n";

static GLuint compile(GLenum type, const char *src) {
  GLuint s = glCreateShader(type);
  glShaderSource(s, 1, &src, NULL);
  glCompileShader(s);
  GLint ok = 0;
  glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
  if (!ok) {
    char log[1024];
    glGetShaderInfoLog(s, sizeof log, NULL, log);
    fprintf(stderr, "shader: %s\n", log);
  }
  return s;
}

static int dump_ppm(const char *path, int w, int h) {
  unsigned char *rgba = (unsigned char *)malloc((size_t)w * h * 4);
  if (!rgba) return -1;
  glReadPixels(0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, rgba);
  FILE *o = fopen(path, "wb");
  if (!o) {
    free(rgba);
    return -1;
  }
  fprintf(o, "P6\n%d %d\n255\n", w, h);
  /* flip Y */
  for (int y = h - 1; y >= 0; y--) {
    for (int x = 0; x < w; x++) {
      unsigned char *p = rgba + ((size_t)y * w + x) * 4;
      fputc(p[0], o);
      fputc(p[1], o);
      fputc(p[2], o);
    }
  }
  fclose(o);
  free(rgba);
  fprintf(stderr, "dumped %s\n", path);
  return 0;
}

static int run_x11(Detect *d, int max_frames, const char *dump_path) {
  Display *dpy = XOpenDisplay(d->x_display[0] ? d->x_display : NULL);
  if (!dpy) {
    fprintf(stderr, "no X11 display\n");
    return 2;
  }
  int scr = DefaultScreen(dpy);
  static int vis_attr[] = {GLX_RGBA, GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};
  XVisualInfo *vi = glXChooseVisual(dpy, scr, vis_attr);
  if (!vi) {
    fprintf(stderr, "no GLX visual\n");
    XCloseDisplay(dpy);
    return 3;
  }
  d->glx = 1;
  Colormap cmap = XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
  XSetWindowAttributes swa;
  swa.colormap = cmap;
  swa.event_mask = ExposureMask | KeyPressMask | StructureNotifyMask;
  Window win = XCreateWindow(dpy, RootWindow(dpy, vi->screen), 80, 60, W, H, 0, vi->depth,
                             InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
  char title[256];
  snprintf(title, sizeof title, "KateRTX ZERO2D · %s · %s · no host OS chrome",
           d->rtx ? "RTX" : (d->nvidia ? "NVIDIA" : "GL"), d->path_chosen);
  XStoreName(dpy, win, title);
  XMapWindow(dpy, win);
  GLXContext ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
  glXMakeCurrent(dpy, win, ctx);

  const char *vend = (const char *)glGetString(GL_VENDOR);
  const char *rend = (const char *)glGetString(GL_RENDERER);
  const char *ver = (const char *)glGetString(GL_VERSION);
  if (vend) snprintf(d->gl_vendor, sizeof d->gl_vendor, "%s", vend);
  if (rend) {
    snprintf(d->gl_renderer, sizeof d->gl_renderer, "%s", rend);
    if (strstr(rend, "RTX") || strstr(rend, "GeForce")) {
      d->nvidia = 1;
      if (strstr(rend, "RTX")) d->rtx = 1;
    }
  }
  if (ver) snprintf(d->gl_version, sizeof d->gl_version, "%s", ver);

  GLuint vs = compile(GL_VERTEX_SHADER, VS);
  GLuint fs = compile(GL_FRAGMENT_SHADER, FS);
  GLuint prog = glCreateProgram();
  glAttachShader(prog, vs);
  glAttachShader(prog, fs);
  glLinkProgram(prog);
  GLint linked = 0;
  glGetProgramiv(prog, GL_LINK_STATUS, &linked);
  if (!linked) {
    char log[1024];
    glGetProgramInfoLog(prog, sizeof log, NULL, log);
    fprintf(stderr, "link: %s\n", log);
  }
  GLint loc_t = glGetUniformLocation(prog, "u_time");
  GLint loc_r = glGetUniformLocation(prog, "u_rtx");
  GLint loc_v = glGetUniformLocation(prog, "u_view");

  Atom wm_delete = XInternAtom(dpy, "WM_DELETE_WINDOW", False);
  XSetWMProtocols(dpy, win, &wm_delete, 1);

  int frame = 0;
  int running = 1;
  struct timespec t0;
  clock_gettime(CLOCK_MONOTONIC, &t0);

  fprintf(stderr, "ZERO2D window up · GL %s · %s · rtx=%d\n", d->gl_version, d->gl_renderer, d->rtx);

  while (running) {
    while (XPending(dpy)) {
      XEvent ev;
      XNextEvent(dpy, &ev);
      if (ev.type == KeyPress) {
        KeySym ks = XLookupKeysym(&ev.xkey, 0);
        if (ks == XK_Escape || ks == XK_q) running = 0;
      } else if (ev.type == ClientMessage) {
        if ((Atom)ev.xclient.data.l[0] == wm_delete) running = 0;
      }
    }
    struct timespec now;
    clock_gettime(CLOCK_MONOTONIC, &now);
    float t = (float)(now.tv_sec - t0.tv_sec) + (now.tv_nsec - t0.tv_nsec) * 1e-9f;

    glViewport(0, 0, W, H);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(prog);
    glUniform1f(loc_t, t);
    glUniform1f(loc_r, d->rtx ? 1.f : 0.f);
    float view = fmodf(floorf(t / 3.f), 3.f); /* cycle front/side/over_shoulder */
    glUniform1f(loc_v, view);
    glDrawArrays(GL_TRIANGLES, 0, 3);
    glXSwapBuffers(dpy, win);
    frame++;
    if (max_frames > 0 && frame >= max_frames) {
      if (dump_path && dump_path[0]) dump_ppm(dump_path, W, H);
      running = 0;
    }
    /* ~60fps thrift sleep */
    usleep(16000);
  }

  glXMakeCurrent(dpy, None, NULL);
  glXDestroyContext(dpy, ctx);
  XDestroyWindow(dpy, win);
  XCloseDisplay(dpy);
  fprintf(stderr, "ZERO2D frames=%d done\n", frame);
  return 0;
}

int main(int argc, char **argv) {
  int detect_only = 0;
  int max_frames = 0;
  const char *dump = NULL;
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "--detect") || !strcmp(argv[i], "-d"))
      detect_only = 1;
    else if (!strcmp(argv[i], "--frames") && i + 1 < argc)
      max_frames = atoi(argv[++i]);
    else if (!strcmp(argv[i], "--dump") && i + 1 < argc)
      dump = argv[++i];
    else if (!strcmp(argv[i], "--help") || !strcmp(argv[i], "-h")) {
      fprintf(stderr,
              "krtx-zero2d · KateRTX 2D zero-cost demo\n"
              "  --detect           JSON detect Wayland/X11/GL/RTX\n"
              "  --frames N         run N frames then quit\n"
              "  --dump path.ppm    write frame (use with --frames)\n"
              "  Esc/q quit window\n");
      return 0;
    }
  }

  Detect d;
  detect_fill(&d);

  if (detect_only) {
    /* try open GL briefly for strings if X11 */
    if (d.x11) {
      Display *dpy = XOpenDisplay(d.x_display[0] ? d.x_display : NULL);
      if (dpy) {
        static int vis_attr[] = {GLX_RGBA, GLX_DOUBLEBUFFER, None};
        XVisualInfo *vi = glXChooseVisual(dpy, DefaultScreen(dpy), vis_attr);
        if (vi) {
          d.glx = 1;
          Colormap cmap =
              XCreateColormap(dpy, RootWindow(dpy, vi->screen), vi->visual, AllocNone);
          XSetWindowAttributes swa;
          swa.colormap = cmap;
          swa.event_mask = 0;
          Window win =
              XCreateWindow(dpy, RootWindow(dpy, vi->screen), 0, 0, 8, 8, 0, vi->depth,
                            InputOutput, vi->visual, CWColormap, &swa);
          GLXContext ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
          glXMakeCurrent(dpy, win, ctx);
          const char *vend = (const char *)glGetString(GL_VENDOR);
          const char *rend = (const char *)glGetString(GL_RENDERER);
          const char *ver = (const char *)glGetString(GL_VERSION);
          if (vend) snprintf(d.gl_vendor, sizeof d.gl_vendor, "%s", vend);
          if (rend) {
            snprintf(d.gl_renderer, sizeof d.gl_renderer, "%s", rend);
            if (strstr(rend, "RTX")) d.rtx = 1;
            if (strstr(rend, "NVIDIA") || strstr(rend, "GeForce")) d.nvidia = 1;
          }
          if (ver) snprintf(d.gl_version, sizeof d.gl_version, "%s", ver);
          glXMakeCurrent(dpy, None, NULL);
          glXDestroyContext(dpy, ctx);
          XDestroyWindow(dpy, win);
        }
        XCloseDisplay(dpy);
      }
    }
    detect_print_json(&d);
    return 0;
  }

  if (!d.x11) {
    detect_print_json(&d);
    fprintf(stderr, "cannot open window · no X11\n");
    return 1;
  }

  /* prefer NVIDIA GLX when RTX present */
  if (d.nvidia || d.rtx) {
    setenv("__GLX_VENDOR_LIBRARY_NAME", "nvidia", 0);
  }

  return run_x11(&d, max_frames, dump);
}
