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

matplotlib.use("TkAgg")

# ----------------- HDGL Machine -----------------
class HDGLMachine:
    def __init__(self):
        self.phi = 1.6180339887
        self.phi_power_phi = 2.6180339887
        self.P3 = 4.2360679775
        self.P4 = 6.8541019662
        self.pi = math.pi
        self.inv_phi = 0.6180339887
        self.inv_P3 = 0.2360679775
        self.current_state = 0.0
        self.recursion_active = False
        self.seed = 1.0
        self.use_hdgl_base = False  # toggle between traditional base-10 and HDGL base

    # HDGL operations
    def hdgl_add(self, a, b):
        return a + b + self.phi

    def hdgl_sub(self, a, b):
        return a - b + self.inv_phi

    def hdgl_mul(self, a, b):
        return a * b + self.P3

    def hdgl_div(self, a, b):
        if b == 0:
            return float('inf')
        return a / b + self.inv_P3

    def get_primitive(self, name):
        return {
            "phi": self.phi,
            "phi^phi": self.phi_power_phi,
            "pi": self.pi,
            "P3": self.P3,
            "P4": self.P4
        }.get(name, 0)

    # Expression evaluation
    def evaluate_expression(self, expr):
        for key in ["phi^phi","phi","pi","P3","P4"]:
            expr = expr.replace(key,f"({self.get_primitive(key)})")
        # Use HDGL arithmetic if hdgl base is active
        if self.use_hdgl_base:
            # tokenize
            tokens = re.findall(r"\d+\.\d+|\d+|[+\-*/]", expr)
            if not tokens: return 0.0
            def parse(t): return float(t) if t[0].isdigit() else t
            tokens = [parse(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 float(tokens[0])
        else:
            # Base-10 eval
            try: return float(eval(expr,{"__builtins__":None,"math":math,"sin":math.sin,"cos":math.cos}))
            except: return float('nan')

    # Harmonic waveform
    def compute_harmonic_state(self,t):
        return np.sin(t*self.phi)*self.seed

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

    def toggle_recursion(self):
        self.recursion_active = not self.recursion_active

    def toggle_base(self):
        self.use_hdgl_base = not self.use_hdgl_base

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

# ----------------- GUI -----------------
class HDGLSuperCalculator:
    def __init__(self,root):
        self.root=root
        self.root.title("HDGL Super 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)

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

        # HDGL primitives + controls
        func_frame = ttk.Frame(root)
        func_frame.pack(padx=10,pady=5)
        funcs = ["phi","phi^phi","pi","P3","P4","Recursion","Toggle Base"]
        for i,t in enumerate(funcs):
            if t=="Recursion":
                b = ttk.Button(func_frame,text=t,command=self.toggle_recursion)
            elif t=="Toggle Base":
                b = ttk.Button(func_frame,text=t,command=self.toggle_base)
            else:
                b = ttk.Button(func_frame,text=t,command=lambda x=t:self.on_button(x))
            b.grid(row=0,column=i,sticky="nsew",padx=2,pady=2)

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

        # Analog waveform
        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=="=":
            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)
        elif char=="C":
            self.expression=""
            self.display.delete(0,tk.END)
        else:
            self.expression+=str(char)
            self.display.delete(0,tk.END)
            self.display.insert(0,self.expression)

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

    def toggle_base(self):
        self.machine.toggle_base()

    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(100,self.update_plot) # faster refresh for smooth waveform

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