import tkinter as tk
from tkinter import ttk
import math
import matplotlib
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
import re

matplotlib.use("TkAgg")

# ----------------- HDGL Machine -----------------
class HDGLMachine:
    def __init__(self):
        self.upper_field = {
            "phi": 1.6180339887,
            "phi_power_phi": 2.6180339887,
            "pi": 3.1415926535,
        }
        self.analog_dims = {
            "P3": 4.2360679775,
            "P4": 6.8541019662,
        }
        self.lower_field = {
            "inv_phi": 0.6180339887,
            "inv_P3": 0.2360679775,
        }
        self.void = 0.0
        self.current_state = self.void
        self.dimension = 1.0
        self.recursion_active = False
        self.seed = 1.0

    # ---- HDGL Bootstrap Arithmetic ----
    def hdgl_add(self, a, b):
        return a + b + self.upper_field["phi"]

    def hdgl_sub(self, a, b):
        return a - b + self.lower_field["inv_phi"]

    def hdgl_mul(self, a, b):
        return a * b + self.analog_dims["P3"]

    def hdgl_div(self, a, b):
        if b == 0:
            return float("inf")
        return a / b + self.lower_field["inv_P3"]

    # ---- Extended functions ----
    def hdgl_phi(self):
        return self.upper_field["phi"]

    def hdgl_phi_power_phi(self):
        return self.upper_field["phi_power_phi"]

    def hdgl_pi(self):
        return self.upper_field["pi"]

    def hdgl_P3(self):
        return self.analog_dims["P3"]

    def hdgl_P4(self):
        return self.analog_dims["P4"]

    # ---- Expression evaluator ----
    def evaluate_expression(self, expr: str):
        # replace function names with actual values
        expr = expr.replace("phi", str(self.hdgl_phi()))
        expr = expr.replace("phi^phi", str(self.hdgl_phi_power_phi()))
        expr = expr.replace("pi", str(self.hdgl_pi()))
        expr = expr.replace("P3", str(self.hdgl_P3()))
        expr = expr.replace("P4", str(self.hdgl_P4()))

        # tokenize numbers and operators
        tokens = re.findall(r"\d+\.\d+|\d+|[+\-*/]", expr)
        if not tokens:
            return 0.0

        def parse_num(tok): return float(tok) if re.match(r"\d", tok) else tok
        tokens = [parse_num(t) for t in tokens]

        # *, /
        i = 0
        while i < len(tokens):
            if tokens[i] == "*":
                res = self.hdgl_mul(tokens[i-1], tokens[i+1])
                tokens[i-1:i+2] = [res]; i -= 1
            elif tokens[i] == "/":
                res = self.hdgl_div(tokens[i-1], tokens[i+1])
                tokens[i-1:i+2] = [res]; i -= 1
            else:
                i += 1

        # +, -
        i = 0
        while i < len(tokens):
            if tokens[i] == "+":
                res = self.hdgl_add(tokens[i-1], tokens[i+1])
                tokens[i-1:i+2] = [res]; i -= 1
            elif tokens[i] == "-":
                res = self.hdgl_sub(tokens[i-1], tokens[i+1])
                tokens[i-1:i+2] = [res]; i -= 1
            else:
                i += 1

        return tokens[0]

    # ---- Machine dynamics ----
    def toggle_recursion(self):
        self.recursion_active = not self.recursion_active

    def compute_harmonic_state(self, t):
        state = math.sin(t * self.upper_field["phi"]) * self.seed
        return state

    def step(self, t):
        if self.recursion_active:
            self.current_state = self.compute_harmonic_state(t) * 2
        else:
            self.current_state = self.compute_harmonic_state(t)
        return self.current_state

    def kick(self, value: float):
        self.seed = value if value != 0 else 1.0

# ----------------- GUI -----------------
class HDGLCalculatorApp:
    def __init__(self, root):
        self.root = root
        self.root.title("HDGL Machine Calculator")

        self.machine = HDGLMachine()
        self.expression = ""

        # Display
        self.display = tk.Entry(root, font=("Consolas", 18), bd=5, relief="sunken", justify="right")
        self.display.pack(fill="x", padx=10, pady=5)

        # Calculator buttons
        btns_frame = ttk.Frame(root)
        btns_frame.pack(padx=10, pady=5)

        buttons = [
            "7", "8", "9", "/",
            "4", "5", "6", "*",
            "1", "2", "3", "-",
            "0", ".", "=", "+"
        ]
        for i, text in enumerate(buttons):
            b = ttk.Button(btns_frame, text=text, command=lambda t=text: self.on_button(t))
            b.grid(row=i//4, column=i%4, sticky="nsew", padx=2, pady=2)

        # HDGL function buttons
        func_frame = ttk.Frame(root)
        func_frame.pack(padx=10, pady=5)
        funcs = ["phi", "phi^phi", "pi", "P3", "P4"]
        for i, text in enumerate(funcs):
            b = ttk.Button(func_frame, text=text, command=lambda t=text: self.on_button(t))
            b.grid(row=0, column=i, sticky="nsew", padx=2, pady=2)

        for i in range(4):
            btns_frame.columnconfigure(i, weight=1)
        for i in range(4):
            btns_frame.rowconfigure(i, weight=1)
        for i in range(len(funcs)):
            func_frame.columnconfigure(i, weight=1)

        # Control buttons
        ctrl_frame = ttk.Frame(root)
        ctrl_frame.pack(padx=10, pady=5, fill="x")
        ttk.Button(ctrl_frame, text="C", command=self.clear).pack(side="left", expand=True, fill="x")
        ttk.Button(ctrl_frame, text="Recursion", command=self.toggle_recursion).pack(side="left", expand=True, fill="x")

        # Waveform Plot
        self.fig, self.ax = plt.subplots(figsize=(6, 3))
        self.canvas = FigureCanvasTkAgg(self.fig, master=root)
        self.canvas.get_tk_widget().pack(fill="both", expand=True)
        self.line, = self.ax.plot([], [], lw=2)
        self.ax.set_xlim(0, 10)
        self.ax.set_ylim(-5, 5)
        self.t_vals = np.linspace(0, 10, 500)
        self.update_plot()

    def on_button(self, char):
        if char == "=":
            try:
                result = self.machine.evaluate_expression(self.expression)
                self.display.delete(0, tk.END)
                self.display.insert(0, str(result))
                self.expression = str(result)
                self.machine.kick(result)
            except Exception:
                self.display.delete(0, tk.END)
                self.display.insert(0, "Error")
                self.expression = ""
        else:
            self.expression += str(char)
            self.display.delete(0, tk.END)
            self.display.insert(0, self.expression)

    def clear(self):
        self.expression = ""
        self.display.delete(0, tk.END)

    def toggle_recursion(self):
        self.machine.toggle_recursion()

    def update_plot(self):
        y_vals = [self.machine.step(t) for t in self.t_vals]
        self.line.set_data(self.t_vals, y_vals)
        self.ax.set_ylim(min(y_vals)-1, max(y_vals)+1)
        self.canvas.draw()
        self.root.after(200, self.update_plot)

# ----------------- Run -----------------
if __name__ == "__main__":
    root = tk.Tk()
    app = HDGLCalculatorApp(root)
    root.mainloop()
