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

# ---------- Globals ----------
window = None
shader_main = None
shader_overlay = None
vao = None
fbo = None
color_tex = None
depth_rb = None

NUM_SLOTS = 256
WIDTH, HEIGHT = 7680, 4320   # 8K internal render (downsample to screen)

# ---------- Shaders ----------
VERT_SRC = """
#version 330
layout(location=0) in vec2 aPos;
out vec2 vUV;
void main() {
    vUV = (aPos+1.0)/2.0;
    gl_Position = vec4(aPos,0,1);
}
"""

FRAG_MAIN = """
#version 330
in vec2 vUV;
out vec4 fragColor;
uniform float iTime;
uniform int numSlots;

const float PHI = 1.6180339887;
const float PI  = 3.14159265359;

void main(){
    vec2 uv = vUV*2.0 - 1.0;
    float r = length(uv);
    float a = atan(uv.y, uv.x);

    float accum = 0.0;
    for(int i=0; i<numSlots; i++){
        float fi = float(i+1);
        accum += sin(iTime*0.2*fi + r*PHI*fi + cos(a*fi*PHI));
    }
    accum /= float(numSlots);

    vec3 col = vec3(
        0.5+0.5*sin(accum*2.0+0.0),
        0.5+0.5*sin(accum*2.0+2.0),
        0.5+0.5*sin(accum*2.0+4.0)
    );
    fragColor = vec4(col,1.0);
}
"""

FRAG_OVERLAY = """
#version 330
in vec2 vUV;
out vec4 fragColor;
uniform sampler2D baseTex;
uniform float iTime;

void main(){
    vec4 base = texture(baseTex, vUV);

    // holographic glyph overlay: ascii-like oscillations
    float gx = floor(vUV.x*80.0);
    float gy = floor(vUV.y*40.0);
    float g = sin(gx*0.2 + gy*0.3 + iTime*3.0);

    vec3 glyph = vec3(0.6+0.4*sin(g*10.0), 0.4+0.6*cos(g*6.0), 0.7+0.3*sin(g*8.0));
    fragColor = mix(base, vec4(glyph,1.0), 0.35);
}
"""

# ---------- Init ----------
def create_quad():
    quad = 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,quad.nbytes,quad,GL_STATIC_DRAW)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,0,ctypes.c_void_p(0))
    return vao

def create_fbo(w,h):
    fbo = glGenFramebuffers(1)
    glBindFramebuffer(GL_FRAMEBUFFER,fbo)

    tex = glGenTextures(1)
    glBindTexture(GL_TEXTURE_2D,tex)
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,None)
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
    glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,tex,0)

    depth = glGenRenderbuffers(1)
    glBindRenderbuffer(GL_RENDERBUFFER,depth)
    glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH24_STENCIL8,w,h)
    glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_STENCIL_ATTACHMENT,GL_RENDERBUFFER,depth)

    if glCheckFramebufferStatus(GL_FRAMEBUFFER)!=GL_FRAMEBUFFER_COMPLETE:
        print("FBO incomplete!")
    glBindFramebuffer(GL_FRAMEBUFFER,0)
    return fbo,tex,depth

def init():
    global vao, shader_main, shader_overlay, fbo, color_tex, depth_rb
    vao = create_quad()
    shader_main = compileProgram(compileShader(VERT_SRC,GL_VERTEX_SHADER),
                                 compileShader(FRAG_MAIN,GL_FRAGMENT_SHADER))
    shader_overlay = compileProgram(compileShader(VERT_SRC,GL_VERTEX_SHADER),
                                    compileShader(FRAG_OVERLAY,GL_FRAGMENT_SHADER))
    fbo,color_tex,depth_rb = create_fbo(WIDTH,HEIGHT)

# ---------- Render ----------
start = time.time()
def display():
    global fbo, color_tex
    t = time.time()-start

    # Pass 1: render into FBO
    glBindFramebuffer(GL_FRAMEBUFFER,fbo)
    glViewport(0,0,WIDTH,HEIGHT)
    glUseProgram(shader_main)
    glUniform1f(glGetUniformLocation(shader_main,"iTime"),t)
    glUniform1i(glGetUniformLocation(shader_main,"numSlots"),NUM_SLOTS)
    glBindVertexArray(vao)
    glDrawArrays(GL_TRIANGLE_STRIP,0,4)

    # Pass 2: overlay to screen
    glBindFramebuffer(GL_FRAMEBUFFER,0)
    glViewport(0,0,glutGet(GLUT_WINDOW_WIDTH),glutGet(GLUT_WINDOW_HEIGHT))
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
    glUseProgram(shader_overlay)
    glUniform1f(glGetUniformLocation(shader_overlay,"iTime"),t)
    glActiveTexture(GL_TEXTURE0)
    glBindTexture(GL_TEXTURE_2D,color_tex)
    glUniform1i(glGetUniformLocation(shader_overlay,"baseTex"),0)
    glBindVertexArray(vao)
    glDrawArrays(GL_TRIANGLE_STRIP,0,4)

    glutSwapBuffers()
    glutPostRedisplay()

# ---------- Main ----------
def main():
    global window
    glutInit()
    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH)
    glutInitWindowSize(1280,720)
    window = glutCreateWindow(b"SuperGlyphs FBO RX480")
    init()
    glutDisplayFunc(display)
    glutMainLoop()

if __name__=="__main__":
    main()
