import sys, time
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,1);
}
"""

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

uniform float cycle;
uniform float omegaTime;
uniform float r_dim;
uniform vec4 D_slots[8]; // 8 vec4 = 32 slots

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

    float val = 0.0;
    for(int i=0;i<8;i++){
        vec4 slot = D_slots[i];
        float Omega = 0.5 + 0.5*sin(omegaTime + float(i)*0.1);
        val += (slot.x + slot.y + slot.z + slot.w) * Omega * r_dim;
    }

    float phase = sin(cycle*0.01 + val);
    fragColor = vec4(val, phase, r, 1.0);
}
"""

# ---------- Globals ----------
window = None
shader = None
vao = None
cycle = 0.0
omega_time = 0.0
r_dim = 0.5  # radial superposition weight

# Precompute lookup tables
phi = 1.6180339887
fib_table = np.array([((phi**n - (-1/phi)**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], dtype=np.float32)

# Compute D_slots[32] for 8 instances x 4 slots
D_slots = np.zeros(32, dtype=np.float32)
for n in range(32):
    F_n = fib_table[n % 128]
    P_n = prime_table[n % len(prime_table)]
    dyadic = 2 ** (n % 16)
    D_slots[n] = np.sqrt(phi * F_n * dyadic * P_n)

D_vec4s = D_slots.reshape(8,4)

# ---------- OpenGL Init ----------
def init_gl():
    global shader, vao
    shader = compileProgram(compileShader(VERTEX_SRC, GL_VERTEX_SHADER),
                            compileShader(FRAGMENT_SRC, GL_FRAGMENT_SHADER))

    # Fullscreen quad
    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)
    loc = glGetUniformLocation(shader, "D_slots")
    glUniform4fv(loc, 8, D_vec4s.flatten())
    glUniform1f(glGetUniformLocation(shader, "r_dim"), r_dim)

# ---------- 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 Superposition (Single-Pass)")
    init_gl()
    glutDisplayFunc(display)
    glutIdleFunc(idle)
    glutMainLoop()

if __name__=="__main__":
    main()
