import math
import tkinter as tk
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import cm
import numpy as np

# --- HDGL Machine ---
class HDGLMachine:
    def __init__(self):
        # Core constants
        self.upper_field = {"phi": 1.6180339887, "phi_power_phi": 2.6180339887,
                            "pi": 3.1415926535, "P3": 4.2360679775}
        self.analog_dims = {"dim_switch": 1.0, "P7": 29.0344465435}
        self.lower_field = {"inv_phi": 0.6180339887, "inv_P3": 0.2360679775}
        self.void = 0.0
        self.current_state = self.void
        self.dimension = self.analog_dims["dim_switch"]
        self.recursion_active = False
        self.use_hdgl_base = True
        self.seed = 0.0

        # Primitive weights for 4D display
        self.primitive_weights = {
            'phi_mod': 1.0, 'F_n_mod': 1.0, 'P_n_mod': 1.0, '2^n_mod': 1.0
        }

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

    def toggle_dimension(self):
        self.dimension = 1.0 if self.dimension != 1.0 else 0.0
        print(f"Dimensionality: {'2D' if self.dimension==1.0 else '1D'}")

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

    def compute_harmonic_state(self, t):
        phi = self.upper_field["phi"]
        phi_phi = self.upper_field["phi_power_phi"]
        pi = self.upper_field["pi"]
        P3 = self.upper_field["P3"]

        # Base harmonic state
        state = self.void
        for val in self.upper_field.values():
            state += val * math.sin(t*phi)
        for val in self.lower_field.values():
            state += val * math.cos(t*self.lower_field["inv_phi"])
        for val in self.analog_dims.values():
            state += val * math.sin(t*phi_phi)
        state *= math.sin(t*pi) if self.dimension==1.0 else math.cos(t*pi)

        # Add calculator input
        state += self.seed

        # Apply weighted primitive modulation
        weight_factor = sum(self.primitive_weights.values())
        state *= (1 + weight_factor*0.05)
        return state

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

# --- HDGL Calculator + 4D Visualizer ---
class HDGLCalculator4D:
    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.zvals, self.colors = [], [], [], []

        # --- Tkinter Window ---
        self.root = tk.Tk()
        self.root.title("HDGL 4D Calculator")

        # Display
        self.expression = ""
        self.display = tk.Entry(self.root, width=30, font=("Helvetica",16))
        self.display.grid(row=0,column=0,columnspan=6)

        # Buttons
        buttons = [
            '7','8','9','/','φ','φ^φ',
            '4','5','6','*','π','P3',
            '1','2','3','-','F_n','2^n',
            '0','.','=','+','Rec','Base'
        ]
        row,col=1,0
        for b in buttons:
            tk.Button(self.root,text=b,width=6,height=2,font=("Helvetica",12),
                      command=lambda x=b:self.on_button(x)).grid(row=row,column=col)
            col+=1
            if col>5: row+=1; col=0

        # --- Sliders for 4D primitives ---
        self.sliders = {}
        slider_primitives = ['phi_mod','F_n_mod','P_n_mod','2^n_mod']
        for i,p in enumerate(slider_primitives):
            tk.Label(self.root,text=p).grid(row=row+i,column=0)
            self.sliders[p] = tk.Scale(self.root, from_=0.0, to=5.0, resolution=0.1, orient=tk.HORIZONTAL, length=200)
            self.sliders[p].set(1.0)
            self.sliders[p].grid(row=row+i,column=1,columnspan=5)

        row += len(slider_primitives)

        # --- Matplotlib 3D Figure ---
        self.fig = plt.figure(figsize=(8,5))
        self.ax = self.fig.add_subplot(111, projection='3d')
        self.ax.set_xlabel("Time")
        self.ax.set_ylabel("HDGL State")
        self.ax.set_zlabel("Recursion/Base")
        self.ax.set_title("HDGL 4D Waveform")

        # Embed in Tkinter
        self.canvas = FigureCanvasTkAgg(self.fig, master=self.root)
        self.canvas.get_tk_widget().grid(row=row,column=0,columnspan=6)

        # Animation
        self.ani = animation.FuncAnimation(self.fig, self.update, interval=self.dt*1000, blit=False)

    def on_button(self, char):
        if char=="=":
            try:
                expr = self.expression
                if self.machine.use_hdgl_base:
                    expr = expr.replace("φ^φ", str(self.machine.upper_field["phi_power_phi"]))
                    expr = expr.replace("^","**")
                    expr = expr.replace("φ", str(self.machine.upper_field["phi"]))
                    expr = expr.replace("π", str(self.machine.upper_field["pi"]))
                    expr = expr.replace("P3", str(self.machine.upper_field["P3"]))
                    expr = expr.replace("F_n","5")
                    expr = expr.replace("2^n","2**3")
                self.machine.seed = eval(expr)
                self.display.delete(0, tk.END)
                self.display.insert(0, str(self.machine.seed))
            except Exception as e:
                print("Error:",e)
            self.expression=""
        elif char=="Rec":
            self.machine.toggle_recursion()
        elif char=="Base":
            self.machine.toggle_base()
        else:
            self.expression+=char
            self.display.delete(0,tk.END)
            self.display.insert(0,self.expression)

    def update(self, frame):
        self.t += self.dt

        # Update primitive weights from sliders
        for p, s in self.sliders.items():
            self.machine.primitive_weights[p] = s.get()

        val = self.machine.step(self.t)
        self.times.append(self.t)
        self.values.append(val)
        self.zvals.append(1 if self.machine.recursion_active else 0)
        weight_sum = sum(self.machine.primitive_weights.values())
        self.colors.append(weight_sum)  # color by sum of primitives

        if len(self.times) > self.window:
            self.times.pop(0)
            self.values.pop(0)
            self.zvals.pop(0)
            self.colors.pop(0)

        self.ax.clear()
        self.ax.set_xlabel("Time")
        self.ax.set_ylabel("HDGL State")
        self.ax.set_zlabel("Recursion/Base")
        self.ax.set_title("HDGL 4D Waveform")

        # Map weight to color
        norm_colors = np.array(self.colors)/max(self.colors) if self.colors else [0]
        cmap = cm.viridis
        for i in range(1,len(self.times)):
            self.ax.plot(self.times[i-1:i+1], self.values[i-1:i+1], self.zvals[i-1:i+1],
                         color=cmap(norm_colors[i]), lw=2)

    def run(self):
        print("Controls: Rec = toggle recursion, Base = toggle Base-10/HDGL")
        self.root.mainloop()


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