import numpy as np

# -----------------------------
# HDGL Channel (Superposition Fusion v2)
# -----------------------------
class HDGLChannel:
    def __init__(self, id):
        self.id = id
        self.pages = []             # list of (data, priority)
        self.tension = 0.0

    def assign_page(self, page_data: bytes, priority=0.5):
        self.pages.append((bytearray(page_data), priority))
        self.update_tension()

    def operate(self):
        if not self.pages:
            return

        # Aggregate page data using harmonic fusion
        fused = np.zeros(len(self.pages[0][0]), dtype=np.float64)
        total_weight = 0.0
        for page, prio in self.pages:
            weight = 1.0 + prio  # higher priority gets stronger influence
            fused += np.frombuffer(page, dtype=np.uint8) * weight
            total_weight += weight

        fused = (fused / total_weight) % 256  # normalize to uint8 range

        # Distribute fused data back into all pages
        fused_bytes = bytearray(fused.astype(np.uint8))
        for i in range(len(self.pages)):
            self.pages[i] = (fused_bytes, self.pages[i][1])

        self.update_tension()

    def update_tension(self):
        # Tension proportional to total weighted size of all pages
        self.tension = sum(len(p) * (1.0 + prio) for p, prio in self.pages) / 4096.0

    def swap_out_pages(self):
        # Evict low-priority pages if tension too high
        removed = []
        self.pages.sort(key=lambda x: x[1])  # ascending priority
        while self.tension > 4.0 and self.pages:
            removed.append(self.pages.pop(0))
            self.update_tension()
        return removed

# -----------------------------
# HDGL Lattice (Dynamic + Fusion)
# -----------------------------
class HDGLLattice:
    def __init__(self, initial_channels=16, max_channels=64):
        self.channels = [HDGLChannel(i) for i in range(initial_channels)]
        self.max_channels = max_channels
        self.next_id = initial_channels

    def map_page(self, page_data: bytes, priority=0.5):
        # Select channel with lowest tension
        tensions = [ch.tension for ch in self.channels]
        min_idx = int(np.argmin(tensions))
        ch = self.channels[min_idx]

        if ch.tension > 3.0 and len(self.channels) < self.max_channels:
            # Auto-expand lattice
            new_ch = HDGLChannel(self.next_id)
            self.next_id += 1
            new_ch.assign_page(page_data, priority)
            self.channels.append(new_ch)
        else:
            ch.assign_page(page_data, priority)

    def operate_all(self):
        swapped_pages = []
        for ch in self.channels:
            ch.operate()
            swapped_pages.extend(ch.swap_out_pages())

        # Optionally perform lattice-wide harmonic fusion
        tensions = np.array([ch.tension for ch in self.channels])
        fused_tensions = (tensions + np.roll(tensions, 1) + np.roll(tensions, -1)) / 3
        for i, ch in enumerate(self.channels):
            ch.tension = fused_tensions[i]

        # Remap swapped pages
        for page, prio in swapped_pages:
            self.map_page(page, priority=prio)

# -----------------------------
# Synthetic Device Pages
# -----------------------------
def generate_device_pages():
    devices = {
        "kernel": (64, 1.0),
        "initramfs": (128, 0.8),
        "CPU": (32, 0.95),
        "GPU": (32, 0.9),
        "RAM": (64, 0.7),
        "swap": (16, 0.2),
        "network": (8, 0.5)
    }
    pages = []
    for dev, (count, prio) in devices.items():
        for _ in range(count):
            data = bytes(np.random.randint(0, 256, 4096, dtype=np.uint8))
            pages.append((data, prio))
    return pages

# -----------------------------
# Main Execution
# -----------------------------
if __name__ == "__main__":
    lattice = HDGLLattice(initial_channels=16, max_channels=64)
    all_pages = generate_device_pages()

    for page_data, prio in all_pages:
        lattice.map_page(page_data, priority=prio)

    # Operate lattice for several cycles
    for cycle in range(5):
        lattice.operate_all()
        print(f"Cycle {cycle}: {len(lattice.channels)} channels active")
        for ch in lattice.channels:
            prios = [p[1] for p in ch.pages]
            print(f"  Channel {ch.id}: {len(ch.pages)} pages, tension {ch.tension:.2f}, max prio {max(prios) if prios else 0:.2f}")
