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

# ---------------- Globals ----------------
window = None
shaderProgram = None
NUM_SUPER = 4096
NUM_INSTANCES = 8192
MAX_SLICE = 8
VIRT_WIDTH = 1024
VIRT_HEIGHT = 1024
t0 = time.time()

# Precompute tables
fibTable = np.array([1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987]*8,dtype=np.float32)
primeTable = np.array([2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53]*8,dtype=np.float32)
phi = 1.6180339887

# ---------------- Shaders ----------------
VERTEX_SHADER = """
#version 330
layout(location=0) in vec2 position;
void main(){ gl_Position = vec4(position,0.0,1.0); }
"""

FRAGMENT_SHADER = f"""
#version 330
out vec4 fragColor;

uniform float t;
uniform int NUM_SUPER;
uniform int NUM_INSTANCES;
uniform int MAX_SLICE;
uniform float phi;
uniform float fibTable[128];
uniform float primeTable[128];

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

void main(){{
    vec2 uv = gl_FragCoord.xy / vec2({VIRT_WIDTH},{VIRT_HEIGHT});
    float r = length(uv-0.5)*2.0;
    float val = 0.0;

    for(int s=0;s<NUM_SUPER;s++){{
        int idx = (int(gl_FragCoord.x)*NUM_SUPER+s) % NUM_INSTANCES;
        val += prismatic_recursion(idx,r);
    }}
    val /= float(NUM_SUPER);

    float phase = sin(t*0.01 + val);
    float slice = mod(gl_FragCoord.x,float(MAX_SLICE));

    // Alpha encodes glyph intensity
    float glyphAlpha = 0.2 + 0.8*abs(sin(t*0.05 + val));
    fragColor = vec4(val, phase, r, glyphAlpha);
}}
"""

# ---------------- OpenGL Init ----------------
def init_gl():
    global shaderProgram
    shaderProgram = compileProgram(
        compileShader(VERTEX_SHADER, GL_VERTEX_SHADER),
        compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
    )

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

    # Set uniform arrays
    glUseProgram(shaderProgram)
    glUniform1fv(glGetUniformLocation(shaderProgram,"fibTable"),128,fibTable)
    glUniform1fv(glGetUniformLocation(shaderProgram,"primeTable"),128,primeTable)
    glUniform1f(glGetUniformLocation(shaderProgram,"phi"),phi)

# ---------------- Glyph Overlay ----------------
font = ImageFont.load_default()
def draw_glyph_overlay(framebuffer):
    """Draw ASCII overlay using alpha channel of framebuffer"""
    img = Image.fromarray(framebuffer)
    w,h = img.size
    draw = ImageDraw.Draw(img)
    step = max(1, w//80)
    for y in range(0,h,step):
        for x in range(0,w,step):
            r,g,b,a = img.getpixel((x,y))
            if a > 128:  # threshold alpha
                glyph = chr(33 + (x+y)%93)
                draw.text((x,y),glyph,fill=(255,255,0))
    img.show()  # For debug; in production, render back to texture

# ---------------- Display Loop ----------------
def display():
    glClear(GL_COLOR_BUFFER_BIT)
    glUseProgram(shaderProgram)
    glUniform1f(glGetUniformLocation(shaderProgram,"t"), time.time()-t0)
    glUniform1i(glGetUniformLocation(shaderProgram,"NUM_SUPER"), NUM_SUPER)
    glUniform1i(glGetUniformLocation(shaderProgram,"NUM_INSTANCES"), NUM_INSTANCES)
    glUniform1i(glGetUniformLocation(shaderProgram,"MAX_SLICE"), MAX_SLICE)
    glDrawArrays(GL_TRIANGLE_STRIP,0,4)

    # Read pixels and draw glyph overlay (CPU side)
    pixels = glReadPixels(0,0,VIRT_WIDTH,VIRT_HEIGHT,GL_RGBA,GL_UNSIGNED_BYTE)
    framebuffer = np.frombuffer(pixels,dtype=np.uint8).reshape(VIRT_HEIGHT,VIRT_WIDTH,4)
    draw_glyph_overlay(framebuffer)

    glutSwapBuffers()
    glutPostRedisplay()

# ---------------- Main ----------------
def main():
    global window
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
    glutInitWindowSize(VIRT_WIDTH,VIRT_HEIGHT)
    window = glutCreateWindow(b"HDGL Prismatic BaseI + Glyph Overlay")
    init_gl()
    glutDisplayFunc(display)
    glutMainLoop()

if __name__=="__main__":
    main()
