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

# ---------- Parameters ----------
NUM_INSTANCES = 64
NUM_SUPER = 64
VIRT_WIDTH = 7680
VIRT_HEIGHT = 4320

cycle = 0.0
omega_time = 0.0

# Precompute 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
], dtype=np.float32)

# ---------- GLSL 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 int NUM_INSTANCES;
uniform int NUM_SUPER;
uniform float fibTable[128];
uniform float primeTable[128];
uniform int VIRT_WIDTH;
uniform int VIRT_HEIGHT;

float prismatic_recursion(int id, float r){
    int inst = id / NUM_SUPER;
    int sub  = id % NUM_SUPER;

    float phi_harm = pow(phi, mod(float(inst+sub),16.0));
    float fib_harm = fibTable[(inst+sub) % 128];
    float dyadic   = float(1 << ((inst+sub) % 16));
    float prime_harm = primeTable[(inst+sub) % 32];
    float Omega = 0.5 + 0.5*sin(omegaTime + float(id)*0.01);
    float r_dim = pow(r, float((id%7)+1));
    return sqrt(phi_harm * fib_harm * dyadic * prime_harm * Omega) * r_dim;
}

void main(){
    // Prismatic mapping (polar)
    vec2 center = vec2(0.5,0.5);
    vec2 uv = texCoord;
    float r = length(uv-center) * 2.0;
    float angle = atan(uv.y-center.y, uv.x-center.x);

    float val = 0.0;
    // Aggregate all instances in one shader pass
    for(int i=0; i<NUM_INSTANCES*NUM_SUPER; i++){
        val += prismatic_recursion(i,r);
    }
    val /= float(NUM_INSTANCES*NUM_SUPER);

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

# ---------- 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"),phi)
    glUniform1fv(glGetUniformLocation(shader,"fibTable"),128,fib_table)
    glUniform1fv(glGetUniformLocation(shader,"primeTable"),32,prime_table)
    glUniform1i(glGetUniformLocation(shader,"NUM_INSTANCES"),NUM_INSTANCES)
    glUniform1i(glGetUniformLocation(shader,"NUM_SUPER"),NUM_SUPER)
    glUniform1i(glGetUniformLocation(shader,"VIRT_WIDTH"),VIRT_WIDTH)
    glUniform1i(glGetUniformLocation(shader,"VIRT_HEIGHT"),VIRT_HEIGHT)

# ---------- 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

def idle():
    glutPostRedisplay()

# ---------- Main ----------
def main():
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE)
    glutInitWindowSize(1920,1080)  # Display window
    glutCreateWindow(b"HDGL Prismatic 8K Superpositions")
    init_gl()
    glutDisplayFunc(display)
    glutIdleFunc(idle)
    glutMainLoop()

if __name__=="__main__":
    main()
