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

# Try importing pyadl
try:
    from pyadl import ADLManager
    adl_available = True
    print("[ADL] pyadl found, using ADL telemetry")
except ImportError:
    adl_available = False
    print("[ADL] pyadl not installed, telemetry fallback")

# Left Brain: prime solver (same as before)
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 + ADL telemetry
window = None
lattice_width = 128
num_instances = 1000
num_instances_max = 50000
glow_phase = 0.0
solver = PrimeSolver()

# Auto-tune params
target_util = 0.85
target_margin = 0.05
scale_step = 250

# Telemetry variables
gpu_util = 0.0
gpu_clock = 0.0
gpu_mem_used = 0
gpu_mem_total = 0

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

def draw_lattice():
    global glow_phase, num_instances, gpu_util, gpu_clock, gpu_mem_used, gpu_mem_total

    glClear(GL_COLOR_BUFFER_BIT)
    glLoadIdentity()

    # CPU stats
    ops, primes = solver.get_stats()

    # Glow logic
    glow_intensity = min(1.0, ops / 20000.0)
    glow_phase += 0.05
    glow = (math.sin(glow_phase)*0.5 + 0.5) * glow_intensity

    solver.depth_mod = 1 + num_instances / num_instances_max

    # Draw lattice points
    glBegin(GL_POINTS)
    for i in range(num_instances):
        x = (i % lattice_width)/lattice_width * 2 - 1
        y = (i // lattice_width)/lattice_width * 2 - 1
        glColor4f(glow, glow*0.8, glow*0.4, 0.7)
        glVertex2f(x, y)
    glEnd()

    # ADL telemetry if available
    if adl_available:
        try:
            device = ADLManager.getInstance().getDevices()[0]
            gpu_util = device.getCurrentUsage() / 100.0  # likely returns percent, so divide
            gpu_clock = device.getCurrentEngineClock()
            # ADL might allow querying memory usage; if so:
            # gpu_mem_used = device.getCurrentMemoryClock(), etc. or another ADL call
        except Exception as e:
            # fallback
            gpu_util = 0.0
            gpu_clock = 0.0
    else:
        gpu_util = 0.0
        gpu_clock = 0.0

    # Auto-tune using ADL telemetry
    if adl_available:
        if gpu_util < target_util - target_margin:
            num_instances = min(num_instances + scale_step, num_instances_max)
        elif gpu_util > target_util + target_margin:
            num_instances = max(num_instances - scale_step, lattice_width**2)

    else:
        num_instances = min(num_instances + scale_step, num_instances_max)

    draw_hud(num_instances, ops, primes)

    glutSwapBuffers()

def draw_hud(instances, 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: {instances}",
        f"GPU Load: {gpu_util*100:.1f}%",
        f"Clock: {gpu_clock:.2f} MHz",
        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"HDGL + GRA + ADL Telemetry")
    init_gl()
    glutDisplayFunc(draw_lattice)
    glutTimerFunc(0, update, 0)
    glutMainLoop()

if __name__ == "__main__":
    main()
