import tkinter as tk
from tkinter import ttk
import numpy as np
import matplotlib
matplotlib.use("TkAgg")
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pywifi
import time
from collections import deque

# --------------------------
# Parameters
# --------------------------
N_POINTS = 200        # Trajectory length per network
dt = 0.1              # Update interval
max_networks = 8      # Max SSIDs to display
phi = (1 + np.sqrt(5)) / 2

# --------------------------
# Tkinter setup
# --------------------------
root = tk.Tk()
root.title("HDGL Wi-Fi EMF 3D Lattice Visualizer")

# --------------------------
# Matplotlib figure
# --------------------------
fig = plt.figure(figsize=(8,6))
ax = fig.add_subplot(111, projection='3d')
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)

# --------------------------
# Slider frame
# --------------------------
slider_frame = tk.Frame(root)
slider_frame.pack(side=tk.BOTTOM, fill=tk.X)

def make_slider(label, minv, maxv, default, row):
    tk.Label(slider_frame, text=label).grid(row=row, column=0, sticky='w')
    var = tk.DoubleVar(value=default)
    slider = tk.Scale(slider_frame, from_=minv, to=maxv, resolution=0.01,
                      orient=tk.HORIZONTAL, variable=var)
    slider.grid(row=row, column=1, sticky='we')
    return var

morph_var = make_slider("Morph (Polar→Cartesian)", 0, 1, 0, 0)
ampl_var  = make_slider("Amplitude Scale", 0, 2, 1, 1)

# --------------------------
# Lattice setup
# --------------------------
def lattice_coords(N, t=0, morph=0):
    """Return base phyllotaxis coordinates"""
    indices = np.arange(N)
    radii = np.sqrt(indices)
    angles = indices * 2*np.pi/phi
    zs = np.linspace(-1,1,N)
    x_polar = radii * np.cos(angles)
    y_polar = radii * np.sin(angles)
    x_cart = np.linspace(-1,1,N)
    y_cart = np.linspace(-1,1,N)
    z_cart = np.linspace(-1,1,N)
    x = x_polar*(1-morph) + x_cart*morph
    y = y_polar*(1-morph) + y_cart*morph
    z = zs*(1-morph) + z_cart*morph
    return x, y, z

# --------------------------
# Wi-Fi scanning
# --------------------------
wifi = pywifi.PyWiFi()
iface = wifi.interfaces()[0]

network_buffers = {}  # SSID -> deque of coords
colors = plt.cm.get_cmap('hsv', max_networks)

def scan_networks():
    """Scan Wi-Fi networks and return dict SSID -> RSSI"""
    iface.scan()
    time.sleep(0.2)  # wait for scan results
    results = iface.scan_results()
    return {net.ssid: net.signal for net in results[:max_networks]}

# --------------------------
# Animation
# --------------------------
lines = {}  # SSID -> Line3D object

def update(frame):
    morph = morph_var.get()
    ampl = ampl_var.get()
    networks = scan_networks()
    x_base, y_base, z_base = lattice_coords(N_POINTS, frame*dt, morph)

    for i, (ssid, rssi) in enumerate(networks.items()):
        # Normalize RSSI for amplitude
        amp_mod = ampl * max(0, min(1, (rssi+100)/60))
        x = x_base*amp_mod
        y = y_base*amp_mod
        z = z_base*amp_mod

        if ssid not in network_buffers:
            network_buffers[ssid] = deque(maxlen=N_POINTS)
        network_buffers[ssid].append((x, y, z))

        # Flatten buffer for plotting
        buf = np.array(network_buffers[ssid])
        x_plot = buf[:,0]
        y_plot = buf[:,1]
        z_plot = buf[:,2]

        if ssid not in lines:
            lines[ssid], = ax.plot(x_plot, y_plot, z_plot, color=colors(i), lw=2)
        else:
            lines[ssid].set_data(x_plot, y_plot)
            lines[ssid].set_3d_properties(z_plot)

    ax.set_xlim(-5,5)
    ax.set_ylim(-5,5)
    ax.set_zlim(-2,2)
    return list(lines.values())

ani = FuncAnimation(fig, update, interval=dt*1000, blit=False)
root.mainloop()
