# eightbinary_gl.py
import sys
import numpy as np
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import compileProgram, compileShader

# Shader sources
VERTEX_SRC = """
#version 330
layout (location = 0) in vec2 pos;
out vec2 texCoord;
void main() {
    texCoord = (pos + 1.0) * 0.5;
    gl_Position = vec4(pos, 0.0, 1.0);
}
"""

FRAGMENT_SRC = """
#version 330
uniform sampler2D stateTex;
uniform int cycle;
in vec2 texCoord;
out vec4 fragColor;

void main() {
    // map pixel x -> lattice slot (0..31)
    int x = int(texCoord.x * 32.0);
    float val = texelFetch(stateTex, ivec2(x,0), 0).r;

    // --- HDGL toy rule: XOR with cycle parity ---
    float next = mod(val + float((cycle + x) % 2), 2.0);

    fragColor = vec4(next, next, next, 1.0); // grayscale
}
"""

# Globals
window = None
shader = None
state_tex = None
cycle = 0

def init_gl():
    global shader, state_tex
    shader = compileProgram(
        compileShader(VERTEX_SRC, GL_VERTEX_SHADER),
        compileShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER)
    )

    # Quad covering screen
    verts = np.array([
        -1, -1,  1, -1,  -1, 1,
         1, -1,  1,  1,  -1, 1
    ], dtype=np.float32)
    vao = glGenVertexArrays(1)
    glBindVertexArray(vao)
    vbo = glGenBuffers(1)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(GL_ARRAY_BUFFER, verts.nbytes, verts, GL_STATIC_DRAW)
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, None)
    glEnableVertexAttribArray(0)

    # Initial state texture (32 slots, 1 row, single channel float)
    init_state = np.zeros((1,32), dtype=np.float32)
    init_state[0,0] = 1.0  # seed one "on" bit
    state_tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D, state_tex)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, 32, 1, 0, GL_RED, GL_FLOAT, init_state)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)

def display():
    global cycle
    glClear(GL_COLOR_BUFFER_BIT)

    glUseProgram(shader)
    glUniform1i(glGetUniformLocation(shader, "stateTex"), 0)
    glUniform1i(glGetUniformLocation(shader, "cycle"), cycle)

    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D, state_tex)

    glDrawArrays(GL_TRIANGLES, 0, 6)
    glutSwapBuffers()
    cycle += 1

def idle():
    glutPostRedisplay()

def main():
    global window
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
    glutInitWindowSize(640, 480)
    window = glutCreateWindow(b"HDGL GPU Lattice")
    init_gl()
    glutDisplayFunc(display)
    glutIdleFunc(idle)
    glutMainLoop()

if __name__ == "__main__":
    main()
