import os
import sys
import zipfile
import urllib.request
from pathlib import Path
import numpy as np
from rtlsdr import RtlSdr

# -----------------------------
# Configuration
# -----------------------------
DLL_DIR = Path.home() / "Documents" / "Turing-Complete" / "hdgl_sdr_dlls"
RTL_SDR_URL = "https://osmocom.org/attachments/download/1557/Release-RTL-SDR.zip"

CARRIER_FREQ = 100e6  # Example: 100 MHz
SAMPLE_RATE = 2.048e6
GAIN = 'auto'
TICKS = 64  # Number of simulation ticks
STRANDS = 8
SLOTS_PER_STRAND = 4

# -----------------------------
# Step 1: Ensure DLLs
# -----------------------------
DLL_DIR.mkdir(parents=True, exist_ok=True)
zip_path = DLL_DIR / "rtl_sdr.zip"
if not zip_path.exists():
    print("[+] Downloading RTL-SDR binaries...")
    urllib.request.urlretrieve(RTL_SDR_URL, zip_path)

with zipfile.ZipFile(zip_path, 'r') as zip_ref:
    zip_ref.extractall(DLL_DIR)

for dll_name in ["librtlsdr.dll", "libusb-1.0.dll"]:
    dll_path = DLL_DIR / "bin" / dll_name
    if dll_path.exists():
        dest = DLL_DIR / dll_name
        dest.write_bytes(dll_path.read_bytes())

os.environ["PATH"] = f"{DLL_DIR};{os.environ['PATH']}"

# -----------------------------
# Step 2: Initialize SDR
# -----------------------------
try:
    sdr = RtlSdr()
    sdr.sample_rate = SAMPLE_RATE
    sdr.center_freq = CARRIER_FREQ
    sdr.gain = GAIN
    print(f"[+] SDR detected: Vendor={sdr.vendor}, Product={sdr.product}")
except Exception as e:
    print("[!] SDR initialization failed:", e)
    sys.exit(1)

# -----------------------------
# Step 3: Initialize HDGL lattice
# -----------------------------
# Each strand has SLOTS_PER_STRAND analog values
lattice = np.zeros((STRANDS, SLOTS_PER_STRAND))
binary_lattice = np.zeros_like(lattice, dtype=int)

# Example superposition function
def evolve_strand(strand):
    # Simple growth + random noise for demo
    noise = np.random.normal(0, 0.01, SLOTS_PER_STRAND)
    strand += 0.05 + noise
    # Threshold to binary
    binary = (strand > 1.0).astype(int)
    return strand, binary

# -----------------------------
# Step 4: Stream lattice to SDR
# -----------------------------
def lattice_to_signal(lattice):
    # Convert lattice analog values to a real waveform riding a carrier
    t = np.arange(0, 1024) / SAMPLE_RATE
    carrier = np.cos(2 * np.pi * CARRIER_FREQ * t)
    # Sum strands with different weights
    mod_signal = np.zeros_like(carrier)
    for s in range(STRANDS):
        weight = np.mean(lattice[s])
        mod_signal += weight * np.sin(2 * np.pi * 1e3 * t * (s+1))
    # Normalize
    mod_signal /= np.max(np.abs(mod_signal))
    return mod_signal

# -----------------------------
# Step 5: Main Tick Loop
# -----------------------------
for tick in range(TICKS):
    print(f"=== Tick {tick+1} ===")
    for s in range(STRANDS):
        lattice[s], binary_lattice[s] = evolve_strand(lattice[s])
        print(f"Strand {chr(65+s)}: Analog {np.round(lattice[s],3)} | Binary {binary_lattice[s].tolist()}")
    # Generate composite signal
    signal = lattice_to_signal(lattice)
    # Stream to SDR (write_samples expects complex64)
    iq_signal = signal.astype(np.complex64)  # For demonstration
    # Normally use sdr.write_samples(iq_signal) with HackRF or supported device
    # For RTL-SDR (receive only), we cannot transmit — so this simulates preparation
    print(f"Aggregated Lattice (Hex): 0x{int(binary_lattice.flatten().dot(1 << np.arange(STRANDS*SLOTS_PER_STRAND))) :08x}")

# -----------------------------
# Step 6: Cleanup
# -----------------------------
sdr.close()
print("[+] HDGL SDR lattice session complete")
