import sys, time
import numpy as np
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GL.shaders import compileProgram, compileShader

# ---------- Shaders ----------
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, 1);
}
"""

FRAGMENT_SRC = """
#version 330
in vec2 texCoord;
out vec4 fragColor;

uniform float cycle;
uniform float omegaTime;
uniform float phi;
uniform float phiInv;
uniform float fibTable[128];
uniform float primeTable[128];
uniform int numInstances;

float prismatic_slot(int id, float r, float omegaShift, float rBias) {
    float phi_harm = pow(phi, float(id % 16));
    float fib_harm = fibTable[id % 128];
    float dyadic = float(1 << (id % 16));
    float prime_harm = primeTable[id % 128];
    float Omega = 0.5 + 0.5*sin(omegaTime + omegaShift);
    float r_dim = pow(r, rBias);
    return sqrt(phi_harm * fib_harm * dyadic * prime_harm * Omega) * r_dim;
}

void main() {
    float r = length(texCoord - 0.5) * 2.0;
    vec4 accum = vec4(0.0);

    // Superposition: 64 instance slots
    for(int i=0; i<numInstances; i++){
        float shift = float(i) * 0.02;      // phase offset per lattice
        float bias = 0.3 + 0.01*float(i);   // r_dim bias per instance
        float val = prismatic_slot(i, r, shift, bias);

        // pack into RGBA channels cyclically
        if(i % 4 == 0) accum.r += val;
        else if(i % 4 == 1) accum.g += val;
        else if(i % 4 == 2) accum.b += val;
        else accum.a += val;
    }

    fragColor = accum / float(numInstances);
}
"""

# ---------- Globals ----------
window = None
shader = None
vao = None
cycle = 0.0
omega_time = 0.0
num_instances = 64  # superposition slots

fib_table = np.array([((1.6180339887**n - (1-1.6180339887)**n)/np.sqrt(5)) for n in range(128)], dtype=np.float32)
prime_table = np.array([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,
                        137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,
                        269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,
                        419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,
                        571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691], dtype=np.float32)

# ---------- OpenGL Init ----------
def init_gl():
    global shader, vao
    shader = compileProgram(compileShader(VERTEX_SRC, GL_VERTEX_SHADER),
                            compileShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER))
    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)

    glUseProgram(shader)
    glUniform1f(glGetUniformLocation(shader,"phi"), 1.6180339887)
    glUniform1f(glGetUniformLocation(shader,"phiInv"), 0.6180339887)
    glUniform1fv(glGetUniformLocation(shader,"fibTable"),128,fib_table)
    glUniform1fv(glGetUniformLocation(shader,"primeTable"),128,prime_table)
    glUniform1i(glGetUniformLocation(shader,"numInstances"), num_instances)

# ---------- Display ----------
def display():
    global cycle, omega_time
    glClear(GL_COLOR_BUFFER_BIT)
    glUseProgram(shader)
    glUniform1f(glGetUniformLocation(shader,"cycle"), cycle)
    glUniform1f(glGetUniformLocation(shader,"omegaTime"), omega_time)
    glBindVertexArray(vao)
    glDrawArrays(GL_TRIANGLES,0,6)
    glutSwapBuffers()
    cycle += 1.0
    omega_time += 0.05

# ---------- Idle ----------
def idle():
    glutPostRedisplay()

# ---------- Main ----------
def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
    glutInitWindowSize(1280,720)
    glutCreateWindow(b"HDGL Prismatic GPU Fabric")
    init_gl()
    glutDisplayFunc(display)
    glutIdleFunc(idle)
    glutMainLoop()

if __name__=="__main__":
    main()
