"""
Electron-speed computation with zero allocation
"""

from typing import Dict, Any, Optional, List
from collections import deque
import array
import weakref
import time
from electron_speed import max_switch_freq, drift_velocity, transit_time

# Memory chunk definition
class MemoryChunk:
    """Memory chunk tracking at electron speed"""
    def __init__(self, size: int):
        self.size = size
        self.in_use = False
        self.last_access = time.time()

# Memory pool definition  
class MemoryPool:
    """Memory pool for electron-speed operations"""

# Electron physics constants
ELECTRON_FREQ = max_switch_freq  # ~9.80e14 Hz
ELECTRON_VELOCITY = drift_velocity  # m/s
GATE_DELAY = transit_time  # seconds

class ElectronCompute:
    """Pure electron-speed computation handler"""
    def __init__(self):
        self.switch_freq = ELECTRON_FREQ
        self.drift_speed = ELECTRON_VELOCITY
        self.gate_delay = GATE_DELAY
        self.cycle_time = 1.0 / self.switch_freq
        
    def compute_timing(self, operations: int) -> float:
        """Calculate operation timing at electron speed"""
        return operations * self.gate_delay

class ElectronProcessor:
    """Direct electron-speed computation with no allocation"""
    def __init__(self):
        self.compute = ElectronCompute()
        self.op_count = 0
        self.last_op_time = time.time()
        
    def process_data(self, data_size: int) -> dict:
        """Process data at electron switching frequency"""
        start_time = time.time()
        
        # Direct computation at electron speed
        computation = {
            'cycles': data_size,
            'frequency': self.compute.switch_freq,
            'timing': self.compute.compute_timing(data_size),
            'throughput': data_size * self.compute.drift_speed
        }
        
        # Track operation
        self.op_count += 1
        self.last_op_time = time.time()
        
        # Validate electron timing
        elapsed = time.time() - start_time
        if elapsed > self.compute.cycle_time:
            print(f"Warning: Operation exceeded electron cycle time: {elapsed:e} vs {self.compute.cycle_time:e} seconds")
        
        return computation
        
    def allocate_chunk(self, size: int) -> Optional[MemoryChunk]:
        """Allocate a memory chunk of given size"""
        # Find first fit
        for chunk in self.free_chunks:
            if chunk.size >= size:
                # Split chunk if too large
                if chunk.size > size * 2:
                    remaining_size = chunk.size - size
                    new_chunk = MemoryChunk(size=remaining_size)
                    new_chunk.prev = chunk
                    new_chunk.next = chunk.next
                    chunk.next = new_chunk
                    chunk.size = size
                    
                    # Add remaining to free list
                    if remaining_size not in self.free_chunks:
                        self.free_chunks[remaining_size] = deque()
                    self.free_chunks[remaining_size].append(new_chunk)
                
                chunk.is_free = False
                # Create weak reference to track usage
                self._weak_refs[chunk.offset] = weakref.ref(chunk, 
                    lambda ref, offset=chunk.offset: self._cleanup(offset))
                return chunk.offset
                
        return None
                
    def free(self, offset: int):
        """Free allocated memory and merge adjacent free chunks"""
        if offset not in self._weak_refs:
            return
            
        chunk_ref = self._weak_refs[offset]
        chunk = chunk_ref()
        if chunk is None or chunk.is_free:
            return
            
        chunk.is_free = True
        
        # Merge with next chunk if free
        while chunk.next and chunk.next.is_free:
            next_chunk = chunk.next
            chunk.size += next_chunk.size
            chunk.next = next_chunk.next
            if chunk.next:
                chunk.next.prev = chunk
            # Remove next chunk from free list
            self.free_chunks[next_chunk.size].remove(next_chunk)
            if not self.free_chunks[next_chunk.size]:
                del self.free_chunks[next_chunk.size]
                
        # Merge with previous chunk if free
        while chunk.prev and chunk.prev.is_free:
            prev_chunk = chunk.prev
            prev_chunk.size += chunk.size
            prev_chunk.next = chunk.next
            if chunk.next:
                chunk.next.prev = prev_chunk
            chunk = prev_chunk
            
        # Add to free list
        if chunk.size not in self.free_chunks:
            self.free_chunks[chunk.size] = deque()
        self.free_chunks[chunk.size].append(chunk)
        
        # Clean up weak reference
        del self._weak_refs[offset]
        
    def _cleanup(self, offset: int):
        """Callback for when a chunk is garbage collected"""
        self.free(offset)

    def get_fragmentation(self) -> float:
        """Calculate memory fragmentation ratio"""
        free_chunks = sum(len(chunks) for chunks in self.free_chunks.values())
        if free_chunks == 0:
            return 0.0
        largest_free = max(self.free_chunks.keys()) if self.free_chunks else 0
        return 1.0 - (largest_free / self.total_size)

class SharedMemoryPool(MemoryPool):
    """Memory pool optimized for shared memory access patterns"""
    __slots__ = ('block_size', 'block_locks')
    
    def __init__(self, total_size: int, block_size: int = 128):
        super().__init__(total_size)
        self.block_size = block_size
        self.block_locks = array.array('B', [0] * (total_size // block_size))  # Use array for atomic operations

    def atomic_allocate(self, size: int) -> Optional[int]:
        """Thread-safe allocation"""
        offset = self.allocate(size)
        if offset is not None:
            block_idx = offset // self.block_size
            self.block_locks[block_idx] = 1  # Mark as locked
        return offset

    def atomic_free(self, offset: int):
        """Thread-safe deallocation"""
        block_idx = offset // self.block_size
        self.block_locks[block_idx] = 0  # Release lock
        self.free(offset)
