import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

# -----------------------------
# SIMULATION PARAMETERS
# -----------------------------
num_nodes = 16
num_strands = 8
slots_per_strand = 4
lattice_size = (num_strands, slots_per_strand)

sample_rate = 1e4      # faster live feel
duration = 0.1         # seconds per frame
num_bands = 4

env_freqs = [90e3, 100e3, 105e3, 110e3]  # placeholder FM/AM
env_amplitudes = [0.8, 0.5, 0.6, 0.4]

# Analog evolution parameters
phase_scale = 0.2
amplitude_scale = 0.1
noise_scale = 0.02
lfo_rate = 0.5

# -----------------------------
# PHYLLOTAXIS NODE POSITIONS
# -----------------------------
def phyllotaxis_positions(n, c=1.0):
    golden_angle = np.pi * (3 - np.sqrt(5))
    positions = []
    for i in range(n):
        r = c * np.sqrt(i)
        theta = i * golden_angle
        x = r * np.cos(theta)
        y = r * np.sin(theta)
        positions.append((x, y))
    return np.array(positions)

node_positions = phyllotaxis_positions(num_nodes)

# -----------------------------
# NODE LATTICES
# -----------------------------
node_lattices = [np.random.rand(*lattice_size) for _ in range(num_nodes)]
node_phases = [np.random.rand(*lattice_size)*2*np.pi for _ in range(num_nodes)]

# -----------------------------
# EVOLVE LATTICE FUNCTION (ANALOG-FLOW)
# -----------------------------
def evolve_lattice(node_lattices, node_phases, env_freqs, t):
    composite = np.zeros_like(t)
    
    # Environmental signals
    for amp, freq in zip(env_amplitudes, env_freqs):
        composite += amp * np.sin(2*np.pi*freq*t)
    
    # HDGL lattice evolution
    for node_idx, (lattice, phase) in enumerate(zip(node_lattices, node_phases)):
        # LFO modulation
        lfo = np.sin(2*np.pi*lfo_rate*t + node_idx)
        
        # Update lattice amplitudes and phases with continuous analog dynamics
        delta = amplitude_scale*lfo + noise_scale*np.random.randn(*lattice.shape)
        lattice += delta
        lattice = np.clip(lattice, 0, 1)
        
        phase += phase_scale*lfo + noise_scale*np.random.randn(*phase.shape)
        
        # Phyllotaxis weighting
        distance_weight = np.exp(-np.linalg.norm(node_positions[node_idx]))
        
        # Multi-band modulation
        for band_idx, freq in enumerate(env_freqs):
            strand_idx = band_idx % lattice.shape[0]
            for slot_idx in range(lattice.shape[1]):
                composite += lattice[strand_idx, slot_idx] * np.sin(2*np.pi*freq*t + phase[strand_idx, slot_idx]*distance_weight)
    
    composite /= len(node_lattices)
    return composite

# -----------------------------
# LIVE PLOTTING
# -----------------------------
def main():
    t_step = 1.0 / sample_rate
    t = np.arange(0, duration, t_step)
    
    fig, ax = plt.subplots(figsize=(12,4))
    line, = ax.plot([], [], lw=1)
    ax.set_xlim(0, duration*1000)
    ax.set_ylim(-3, 3)
    ax.set_xlabel("Time (ms)")
    ax.set_ylabel("Amplitude")
    ax.set_title("Analog-Flow HDGL Lattice Riding Multi-Band Environment")
    ax.grid(True)
    
    def update(frame):
        nonlocal t
        composite_signal = evolve_lattice(node_lattices, node_phases, env_freqs, t)
        line.set_data(t*1000, composite_signal)
        return line,
    
    ani = FuncAnimation(fig, update, interval=50, blit=True)
    plt.show()

if __name__ == "__main__":
    main()
