import numpy as np
import time
import socket
import pickle

# -------------------------------
# HDGL Lattice Parameters
# -------------------------------
num_strands = 8
slots_per_strand = 4
total_slots = num_strands * slots_per_strand

phi = 1.6180339887
sqrt_phi = np.sqrt(phi)

# Ω scaling per strand
n_base = np.arange(1, num_strands+1)
omega = 1 / (phi**n_base)**7

# Initialize lattice analogs D1-D32
lattice = np.random.uniform(0.5, 1.0, (num_strands, slots_per_strand))
phases = np.random.uniform(0, 2*np.pi, (num_strands, slots_per_strand))

# -------------------------------
# RF Output Parameters
# -------------------------------
fs = 1_000_000        # 1 MHz sample rate (adjust per SDR/DAC)
block_size = 4096
t = np.arange(block_size) / fs

# Each slot has unique carrier frequency
slot_freqs = np.linspace(100e3, 400e3, total_slots)

# -------------------------------
# Networking: Multi-Device Sync
# -------------------------------
UDP_IP = "255.255.255.255"  # Broadcast
UDP_PORT = 5005
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)

def broadcast_lattice(lattice, phases):
    data = pickle.dumps({'lattice': lattice, 'phases': phases})
    sock.sendto(data, (UDP_IP, UDP_PORT))

def receive_lattice():
    sock.settimeout(0.001)
    try:
        data, _ = sock.recvfrom(4096)
        packet = pickle.loads(data)
        return packet['lattice'], packet['phases']
    except:
        return None, None

# -------------------------------
# Generate lattice RF block
# -------------------------------
def generate_rf_block(lattice, phases):
    rf_block = np.zeros(block_size, dtype=np.complex64)
    for idx in range(total_slots):
        strand = idx // slots_per_strand
        slot = idx % slots_per_strand
        carrier = np.exp(1j*(2*np.pi*slot_freqs[idx]*t + phases[strand,slot]))
        amp = lattice[strand,slot] / np.max(lattice)
        rf_block += amp * carrier
    # Normalize
    rf_block /= np.max(np.abs(rf_block))
    return rf_block

# -------------------------------
# Main Streaming Loop
# -------------------------------
try:
    print("HDGL Multi-Device RF Lattice streaming. Press Ctrl+C to stop.")
    while True:
        # Step lattice locally
        lattice_new = np.copy(lattice)
        for s in range(num_strands):
            for i in range(slots_per_strand):
                lattice_new[s,i] += 0.02*omega[s]*(1+0.05*i)
                resonance = np.sum([omega[j]*lattice[j,i] for j in range(num_strands) if j!=s])
                lattice_new[s,i] += 0.01*resonance
                lattice_new[s,i] = lattice_new[s,i] if lattice_new[s,i] > sqrt_phi else lattice_new[s,i]*0.8
                phases[s,i] += 0.05*lattice[s,i]
        lattice[:] = lattice_new

        # Broadcast local lattice to other devices
        broadcast_lattice(lattice, phases)

        # Receive lattices from peers and integrate (simple averaging)
        peer_lattice, peer_phases = receive_lattice()
        if peer_lattice is not None:
            lattice = (lattice + peer_lattice)/2
            phases = (phases + peer_phases)/2

        # Generate RF block
        block = generate_rf_block(lattice, phases)
        # sdr.write_samples(block)  # Send to actual SDR/DAC hardware
        time.sleep(block_size/fs)

except KeyboardInterrupt:
    print("Streaming stopped.")
