import math
import time
import sys
import threading
import os
import tty
import termios

# --- HDGL Machine ---
class HDGLMachine:
    def __init__(self):
        self.phi = 1.6180339887
        self.phi_phi = 2.6180339887
        self.P3 = 4.2360679775
        self.P7 = 29.0344465435
        self.recursionActive = False

    def compute(self, t, channel):
        if channel == 'phi':
            return self.phi * math.sin(t*self.phi)
        elif channel == 'phi_phi':
            return self.phi_phi * math.sin(t*self.phi_phi)
        elif channel == 'P3':
            return self.P3 * math.sin(t*self.P3)
        elif channel == 'recursion':
            val = self.phi*math.sin(t*self.phi) + self.phi_phi*math.cos(t*self.phi_phi)
            return val * (self.P7/self.P3) if self.recursionActive else val
        return 0

    def toggle_recursion(self):
        self.recursionActive = not self.recursionActive
        print(f"Recursion: {'ON' if self.recursionActive else 'OFF'}")

# --- Terminal Input ---
class KeyListener(threading.Thread):
    def __init__(self):
        super().__init__()
        self.daemon = True
        self.last_key = None

    def run(self):
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        tty.setcbreak(fd)
        try:
            while True:
                ch = sys.stdin.read(1)
                self.last_key = ch
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

# --- Oscilloscope Display ---
class HelixOscilloscope:
    def __init__(self, machine, width=80, height=40, helix_count=2):
        self.machine = machine
        self.width = width
        self.height = height
        self.helix_count = helix_count
        self.channels = ['phi','phi_phi','P3','recursion']
        self.channel_idx = 0
        self.zoom = 1.0
        self.history = [[] for _ in range(helix_count)]
        self.max_history = 20

    def run(self):
        listener = KeyListener()
        listener.start()
        t = 0.0
        dt = 0.05
        try:
            while True:
                self.handle_input(listener.last_key)
                self.render(t)
                t += dt
                time.sleep(dt)
        except KeyboardInterrupt:
            print("\nExiting oscilloscope.")

    def handle_input(self, key):
        if key is None:
            return
        if key == 'q':
            sys.exit(0)
        elif key == 'r':
            self.machine.toggle_recursion()
        elif key == '+':
            self.zoom *= 1.1
        elif key == '-':
            self.zoom /= 1.1
        elif key == 'c':
            self.channel_idx = (self.channel_idx + 1) % len(self.channels)

    def render(self, t):
        grid = [[' ']*self.width for _ in range(self.height)]
        cx = self.width//2
        cy = self.height//2
        for h in range(self.helix_count):
            ch = self.channels[(self.channel_idx + h) % len(self.channels)]
            r_base = 10 + h*3
            phase_offset = h * 2*math.pi / self.helix_count
            x = cx + int((r_base + self.machine.compute(t,ch)) * math.cos(t+phase_offset) * self.zoom)
            y = cy + int((r_base + self.machine.compute(t,ch)) * math.sin(t+phase_offset) * self.zoom)
            z = int(self.machine.compute(t,ch)*0.5)
            if 0 <= x < self.width and 0 <= y < self.height:
                char = '.' if z<2 else ':' if z<4 else '*' if z<6 else '#'
                grid[y][x] = char
            # Update history for trailing dots
            self.history[h].append((x,y,z))
            if len(self.history[h])>self.max_history:
                self.history[h].pop(0)
            for hx,hy,hz in self.history[h]:
                if 0 <= hx < self.width and 0 <= hy < self.height:
                    grid[hy][hx] = '.' if hz<2 else ':' if hz<4 else '*' if hz<6 else '#'
        # Render grid
        os.system('clear')
        for row in grid:
            print(''.join(row))
        print("\nControls: q=quit | r=recursion | +=zoom | -=zoom | c=channel | Helices:",self.helix_count)

# --- Run ---
if __name__ == "__main__":
    machine = HDGLMachine()
    osc = HelixOscilloscope(machine, width=80, height=40, helix_count=3)
    osc.run()
