import tkinter as tk
from tkinter import ttk
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
import math
import re

matplotlib.use("TkAgg")

# --- HDGL Machine ---
class HDGLMachine:
    def __init__(self):
        # Upper Field
        self.upper_field = {
            "prism_state": 105.0,
            "recursion_mode": 99.9999999999,
            "positive_infinite": 9.9999999999,
            "P3": 4.2360679775,
            "pi": 3.1415926535,
            "phi_power_phi": 2.6180339887,
            "phi": 1.6180339887
        }
        # Analog Dimensions
        self.analog_dims = {
            "D8":8.0,"D7":7.0,"D6":6.0,"D5":5.0,"D4":4.0,"D3":3.0,"D2":2.0,"D1":1.0,
            "dim_switch":1.0,
            "P4":6.8541019662,"P5":11.0901699437,"P6":17.94427191,"P7":29.0344465435
        }
        # Lower Field
        self.lower_field = {
            "negative_infinite":1e-10,
            "inv_P7":0.0344465435,
            "inv_P6":0.05572809,
            "inv_P5":0.0901699437,
            "inv_P4":0.1458980338,
            "inv_P3":0.2360679775,
            "inv_phi_power_phi":0.3819660113,
            "inv_phi":0.6180339887
        }

        self.void = 0.0
        self.current_state = self.void
        self.dimension = self.analog_dims["dim_switch"]
        self.recursion_active = False
        self.use_hdgl_base = False
        self.seed = 1.0

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

    # Toggle dimension
    def toggle_dimension(self):
        self.dimension = 1.0 if self.dimension !=1.0 else 0.0
        print(f"Dimension: {'2D double-helix' if self.dimension==1.0 else '1D'}")

    # Toggle base
    def toggle_base(self):
        self.use_hdgl_base = not self.use_hdgl_base
        print(f"HDGL Base: {'ON' if self.use_hdgl_base else 'Base-10'}")

    # HDGL 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.upper_field["P3"]
    def hdgl_div(self,a,b): return float('inf') if b==0 else a/b+self.lower_field["inv_P3"]

    # Primitive getter
    def get_primitive(self,name):
        return {
            "phi": self.upper_field["phi"],
            "phi^phi": self.upper_field["phi_power_phi"],
            "pi": self.upper_field["pi"],
            "P3": self.upper_field["P3"],
            "P4": self.analog_dims["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)})")

        if self.use_hdgl_base:
            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]=="*":
                    tokens[i-1:i+2]=[self.hdgl_mul(tokens[i-1],tokens[i+1])]; i-=1
                elif tokens[i]=="/":
                    tokens[i-1:i+2]=[self.hdgl_div(tokens[i-1],tokens[i+1])]; i-=1
                else: i+=1
            # +,-
            i=0
            while i<len(tokens):
                if tokens[i]=="+":
                    tokens[i-1:i+2]=[self.hdgl_add(tokens[i-1],tokens[i+1])]; i-=1
                elif tokens[i]=="-":
                    tokens[i-1:i+2]=[self.hdgl_sub(tokens[i-1],tokens[i+1])]; i-=1
                else: i+=1
            return float(tokens[0])
        else:
            try: return float(eval(expr,{"__builtins__":None,"math":math,"sin":math.sin,"cos":math.cos}))
            except: return float('nan')

    # Compute harmonic state
    def compute_harmonic_state(self,t):
        state=self.void
        for v in self.upper_field.values(): state+=v*math.sin(t*self.upper_field["phi"])
        for v in self.lower_field.values(): state+=v*math.cos(t*self.lower_field["inv_phi"])
        for v in self.analog_dims.values(): state+=v*math.sin(t*self.upper_field["phi_power_phi"])
        if self.dimension==1.0: state*=math.sin(t*self.upper_field["pi"])
        else: state*=math.cos(t*self.upper_field["pi"])
        return state

    def step(self,t):
        if self.recursion_active:
            self.current_state=self.compute_harmonic_state(t)
            self.current_state*=self.analog_dims["P7"]/self.lower_field["inv_P7"]
        else:
            self.current_state=self.compute_harmonic_state(t)
        return self.current_state

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

# --- GUI + Calculator ---
class HDGLSuperVisualizer:
    def __init__(self,machine,dt=0.05,window=200):
        self.machine=machine
        self.dt=dt
        self.t=0.0
        self.window=window
        self.times=[]
        self.values=[]
        self.expression=""

        # Tkinter
        self.root=tk.Tk()
        self.root.title("HDGL Super Visualizer + Calculator")

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

        # Buttons
        btn_frame=ttk.Frame(self.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)
        for i in range(5):
            btn_frame.columnconfigure(i,weight=1)
            btn_frame.rowconfigure(i,weight=1)

        # Primitives + Controls
        func_frame=ttk.Frame(self.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.machine.toggle_recursion)
            elif t=="Toggle Base": b=ttk.Button(func_frame,text=t,command=self.machine.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(len(funcs)): func_frame.columnconfigure(i,weight=1)

        # Matplotlib figure
        self.fig,self.ax=plt.subplots(figsize=(10,5))
        self.canvas=matplotlib.backends.backend_tkagg.FigureCanvasTkAgg(self.fig,master=self.root)
        self.canvas.get_tk_widget().pack(fill="both",expand=True)
        self.line,=self.ax.plot([],[],lw=2)
        self.ax.set_xlabel("Time")
        self.ax.set_ylabel("HDGL State")
        self.ax.set_title("HDGL Machine Live State")
        self.ax.set_xlim(0,self.window*self.dt)
        self.ax.set_ylim(-500,500)

        # Animation
        self.ani=animation.FuncAnimation(self.fig,self.update,interval=self.dt*1000,blit=True)
        self.root.after(50,self.tk_loop)

    # Tkinter mainloop
    def tk_loop(self):
        self.root.update()
        self.root.after(50,self.tk_loop)

    # Button press
    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)

    # Animation update
    def update(self,frame):
        self.t+=self.dt
        val=self.machine.step(self.t)
        self.times.append(self.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.set_xlim(self.times[0],self.times[-1])
        return self.line,

    def run(self):
        print("HDGL Controls: Recursion = toggle, Base = toggle")
        self.root.mainloop()

# --- Run ---
if __name__=="__main__":
    machine=HDGLMachine()
    app=HDGLSuperVisualizer(machine)
    app.run()
