import sys
import time
import math
import threading
import numpy as np
from OpenGL.GL import *
from OpenGL.GLUT import *
from OpenGL.GLU import *

# -------------------------------
# Left Brain: Prime Recursion Solver
# -------------------------------
class PrimeSolver(threading.Thread):
    def __init__(self):
        super().__init__()
        self.daemon = True
        self.running = True
        self.ops_per_sec = 0
        self.primes_found = 0
        self.max_depth = 10000
        self.depth_mod = 1
        self.lock = threading.Lock()

    def run(self):
        while self.running:
            start = time.time()
            count = 0
            primes = 0
            limit = int(min(self.max_depth * self.depth_mod, 200000))
            for n in range(2, limit):
                if self.is_prime(n):
                    primes += 1
                count += 1
            elapsed = max(time.time() - start, 1e-6)
            with self.lock:
                self.ops_per_sec = int(count / elapsed)
                self.primes_found += primes
            time.sleep(0.1)

    def is_prime(self, n):
        if n < 2: return False
        if n % 2 == 0: return n == 2
        r = int(math.sqrt(n))
        for i in range(3, r+1, 2):
            if n % i == 0: return False
        return True

    def get_stats(self):
        with self.lock:
            return self.ops_per_sec, self.primes_found

# -------------------------------
# Right Brain: HDGL Lattice
# -------------------------------
window = None
lattice_width = 128
num_instances = 1000
num_instances_max = 60000  # start lower, will scale
glow_phase = 0.0
solver = PrimeSolver()

scale_step = 500

# -------------------------------
# Fragment Shader (per-pixel HDGL)
# -------------------------------
fragment_shader_src = """
#version 330
out vec4 fragColor;
uniform float cycle;
uniform float glow_phase;
uniform float depth_mod;
uniform int lattice_width;
uniform int num_instances;

float phi = 1.6180339887;

// HDGL slot computation
float hdgl_slot(float val, float r_dim, int x, int y, float omega) {
    float resonance = 0.0;
    if(x % 4 == 0) resonance = 0.1 * sin(cycle * 0.05 + float(y));
    float wave = 0.0;
    if(x % 3 == 0) wave = 0.3;
    else if(x % 3 == 1) wave = 0.0;
    else wave = -0.3;
    float omega_inst = 1.0 / pow(pow(phi, float(y+1)), 7.0);
    float rec = r_dim * val * 0.5 + 0.25 * sin(cycle * r_dim + float(x));
    float new_val = val + omega_inst + resonance + wave + rec + omega * 0.05;
    return new_val > 1.272 ? 1.0 : 0.0;
}

void main() {
    int i = int(gl_FragCoord.y) * lattice_width + int(gl_FragCoord.x);
    if(i >= num_instances) discard;
    int x = i % lattice_width;
    int y = i / lattice_width;
    float r_dim = 0.3 + 0.01 * float(y);
    float val = 0.5;
    float new_val = hdgl_slot(val, r_dim, x, y, sin(glow_phase));
    fragColor = vec4(new_val, new_val, new_val, 1.0);
}
"""

shader_program = None
cycle = 0.0

# -------------------------------
# OpenGL setup
# -------------------------------
def compile_shader(source, shader_type):
    shader = glCreateShader(shader_type)
    glShaderSource(shader, source)
    glCompileShader(shader)
    if not glGetShaderiv(shader, GL_COMPILE_STATUS):
        raise RuntimeError(glGetShaderInfoLog(shader))
    return shader

def init_gl():
    global shader_program
    glClearColor(0.0, 0.0, 0.0, 1.0)
    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    glPointSize(2.0)

    # Compile fragment shader only (full-screen quad)
    shader_program = glCreateProgram()
    frag_shader = compile_shader(fragment_shader_src, GL_FRAGMENT_SHADER)
    glAttachShader(shader_program, frag_shader)
    glLinkProgram(shader_program)
    if not glGetProgramiv(shader_program, GL_LINK_STATUS):
        raise RuntimeError(glGetProgramInfoLog(shader_program))
    glUseProgram(shader_program)

def draw_lattice():
    global glow_phase, num_instances, cycle
    glClear(GL_COLOR_BUFFER_BIT)
    glLoadIdentity()

    # CPU stats
    ops, primes = solver.get_stats()
    glow_intensity = min(1.0, ops / 20000.0)
    glow_phase += 0.05
    cycle += 0.03

    # Auto-scale instances
    if num_instances < num_instances_max:
        num_instances += scale_step

    # Update shader uniforms
    glUniform1f(glGetUniformLocation(shader_program, "cycle"), cycle)
    glUniform1f(glGetUniformLocation(shader_program, "glow_phase"), glow_phase)
    glUniform1f(glGetUniformLocation(shader_program, "depth_mod"), solver.depth_mod)
    glUniform1i(glGetUniformLocation(shader_program, "lattice_width"), lattice_width)
    glUniform1i(glGetUniformLocation(shader_program, "num_instances"), num_instances)

    # Draw full-screen quad
    glBegin(GL_QUADS)
    glVertex2f(-1, -1)
    glVertex2f(1, -1)
    glVertex2f(1, 1)
    glVertex2f(-1, 1)
    glEnd()

    # Draw HUD
    draw_hud(ops, primes)

    glutSwapBuffers()

def draw_hud(ops, primes):
    glMatrixMode(GL_PROJECTION)
    glPushMatrix()
    glLoadIdentity()
    gluOrtho2D(0, 800, 0, 800)
    glMatrixMode(GL_MODELVIEW)
    glPushMatrix()
    glLoadIdentity()

    glColor3f(0.2, 1.0, 0.2)
    lines = [
        f"[HDGL] Instances: {num_instances}",
        f"[GRA] Ops/sec: {ops}",
        f"Primes found: {primes}",
        f"Depth mod: {solver.depth_mod:.2f}"
    ]
    y = 770
    for line in lines:
        glRasterPos2f(10, y)
        for ch in line:
            glutBitmapCharacter(GLUT_BITMAP_9_BY_15, ord(ch))
        y -= 18

    glMatrixMode(GL_MODELVIEW)
    glPopMatrix()
    glMatrixMode(GL_PROJECTION)
    glPopMatrix()
    glMatrixMode(GL_MODELVIEW)

def update(value):
    glutPostRedisplay()
    glutTimerFunc(33, update, 0)

def main():
    global window
    solver.start()
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE)
    glutInitWindowSize(800, 800)
    glutInitWindowPosition(0, 0)
    window = glutCreateWindow(b"Left-Brain/Right-Brain HDGL + GRA")
    init_gl()
    glutDisplayFunc(draw_lattice)
    glutTimerFunc(0, update, 0)
    glutMainLoop()

if __name__ == "__main__":
    main()
