import math
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Button, TextBox

# --- HDGL Machine ---
class HDGLMachine:
    def __init__(self):
        self.upper_field = {
            "phi": 1.6180339887,
            "pi": 3.1415926535,
            "phi_power_phi": 2.6180339887,
            "prism_state": 105.0
        }
        self.void = 0.0
        self.recursion_active = False
        self.current_state = 0.0
        self.t = 0.0

    def compute(self, expr: str):
        """Evaluate an expression using HDGL fields as constants."""
        namespace = {**self.upper_field, "sin": math.sin, "cos": math.cos}
        try:
            result = eval(expr, {"__builtins__": {}}, namespace)
        except Exception as e:
            result = f"Error: {e}"
        self.current_state = result if isinstance(result, (int, float)) else 0.0
        return result

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

    def step(self, dt=0.05):
        self.t += dt
        # simple oscillatory update tied to current_state
        factor = self.upper_field["phi"] if self.recursion_active else self.upper_field["pi"]
        self.current_state = math.sin(self.t * factor) * (self.current_state + 1)
        return self.current_state


# --- Unified Calculator + Graph ---
class HDGLApp:
    def __init__(self, machine, dt=0.05, window=200):
        self.machine = machine
        self.dt = dt
        self.window = window
        self.times = []
        self.values = []
        self.expr = ""

        # Main figure layout
        self.fig, (self.ax_graph, self.ax_dummy) = plt.subplots(
            2, 1, figsize=(8, 10), gridspec_kw={"height_ratios": [3, 2]}
        )
        plt.subplots_adjust(bottom=0.3)

        # Graph setup
        self.line, = self.ax_graph.plot([], [], lw=2)
        self.ax_graph.set_xlim(0, self.window * self.dt)
        self.ax_graph.set_ylim(-500, 500)
        self.ax_graph.set_xlabel("Time")
        self.ax_graph.set_ylabel("HDGL State")
        self.ax_graph.set_title("HDGL Machine Live State")

        # Clear dummy axis (used only for widget space)
        self.ax_dummy.axis("off")

        # Display box
        self.text_box_ax = plt.axes([0.1, 0.2, 0.8, 0.07])
        self.text_box = TextBox(self.text_box_ax, "Expr", initial="")

        # Buttons layout
        buttons = [
            ("7", 0.1, 0.12), ("8", 0.25, 0.12), ("9", 0.4, 0.12), ("/", 0.55, 0.12),
            ("4", 0.1, 0.04), ("5", 0.25, 0.04), ("6", 0.4, 0.04), ("*", 0.55, 0.04),
            ("1", 0.7, 0.12), ("2", 0.85, 0.12), ("3", 0.7, 0.04), ("-", 0.85, 0.04),
            ("0", 0.1, -0.04), (".", 0.25, -0.04), ("=", 0.4, -0.04), ("+", 0.55, -0.04),
            ("C", 0.7, -0.04), ("phi", 0.85, -0.04), ("pi", 0.1, -0.12), ("rec", 0.25, -0.12),
        ]

        self.buttons = []
        for label, x, y in buttons:
            ax_btn = plt.axes([x, 0.25 + y, 0.12, 0.06])
            btn = Button(ax_btn, label)
            btn.on_clicked(lambda event, l=label: self.on_press(l))
            self.buttons.append(btn)

    def on_press(self, label):
        if label == "C":
            self.expr = ""
        elif label == "=":
            result = self.machine.compute(self.expr)
            self.expr = str(result)
        elif label == "rec":
            self.expr = self.machine.toggle_recursion()
        else:
            self.expr += label
        self.text_box.set_val(self.expr)

    def update(self, frame):
        val = self.machine.step(self.dt)
        t = self.machine.t
        self.times.append(t)
        self.values.append(val)
        if len(self.times) > self.window:
            self.times.pop(0)
            self.values.pop(0)
        self.line.set_data(self.times, self.values)
        self.ax_graph.set_xlim(self.times[0], self.times[-1])
        return self.line,

    def run(self):
        ani = animation.FuncAnimation(
            self.fig, self.update, blit=True, interval=self.dt * 1000
        )
        plt.show()


# --- Run ---
if __name__ == "__main__":
    machine = HDGLMachine()
    app = HDGLApp(machine)
    print("Controls:")
    print(" Calculator buttons below graph.")
    print(" φ = phi, π = pi, rec = toggle recursion mode.")
    app.run()
