import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
import pywifi
from pywifi import const
import time
import random

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

def scan_networks():
    iface.scan()
    time.sleep(1.0)  # allow time to gather
    results = iface.scan_results()
    return [(res.ssid, res.signal) for res in results]

# --- Parameters ---
dt = 0.05  # seconds per frame
n_particles = 300  # adaptive cloud particles
stable_threshold = 3.0  # RSSI variance threshold for stability
stable_frames = 30  # number of frames before promoting to scaffold

# State
particle_positions = np.random.uniform(-1, 1, (n_particles, 3))
particle_colors = np.random.rand(n_particles, 3)

# Persistent scaffold
scaffold_points = []
scaffold_colors = []
stability_tracker = {}  # key=(ssid), value=(last_signals list)

# --- Matplotlib Setup ---
fig = plt.figure(figsize=(8, 6))
ax = fig.add_subplot(111, projection='3d')
ax.set_facecolor("black")
fig.patch.set_facecolor("black")

scat_cloud = ax.scatter([], [], [], s=10, c=[], alpha=0.7)
scat_scaffold = ax.scatter([], [], [], s=20, c=[], alpha=0.9, marker='^')

ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)
ax.set_zlim(-1, 1)
ax.set_xticks([])
ax.set_yticks([])
ax.set_zticks([])

# --- Update Function ---
def update(frame):
    global particle_positions, particle_colors, scaffold_points, scaffold_colors, stability_tracker

    # Scan Wi-Fi
    networks = scan_networks()

    # --- Adaptive Cloud Update ---
    if networks:
        for i, (ssid, signal) in enumerate(networks[:n_particles]):
            strength = max(-100, signal) / -100.0  # normalize [0..1]
            # Random walk with bias by signal strength
            particle_positions[i] += np.random.normal(0, 0.02, 3) * (0.5 + strength)
            particle_positions[i] = np.clip(particle_positions[i], -1, 1)

            # Color mapping from RSSI
            particle_colors[i] = [1-strength, strength, random.uniform(0, 0.5)]

            # --- Stability Tracker ---
            if ssid not in stability_tracker:
                stability_tracker[ssid] = []
            stability_tracker[ssid].append(signal)

            if len(stability_tracker[ssid]) > stable_frames:
                history = stability_tracker[ssid][-stable_frames:]
                if np.var(history) < stable_threshold:
                    # Promote stable point into scaffold
                    mean_signal = np.mean(history)
                    pos = particle_positions[i] * (0.5 + mean_signal/-100.0)
                    scaffold_points.append(pos)
                    scaffold_colors.append([0.8, 0.8, 1.0])  # bluish for static
                    stability_tracker[ssid] = []  # reset

    # --- Update Cloud Scatter ---
    scat_cloud._offsets3d = (particle_positions[:, 0], particle_positions[:, 1], particle_positions[:, 2])
    scat_cloud.set_color(particle_colors)

    # --- Update Scaffold Scatter ---
    if scaffold_points:
        pts = np.array(scaffold_points)
        scat_scaffold._offsets3d = (pts[:, 0], pts[:, 1], pts[:, 2])
        scat_scaffold.set_color(scaffold_colors)

    return scat_cloud, scat_scaffold

ani = FuncAnimation(fig, update, interval=dt*1000, blit=False)
plt.show()
