# shared/uart_comm.py
import _thread
from machine import UART
import time

class SafeUART:
    def __init__(self, uart_id, tx_pin, rx_pin, baudrate=115200):
        # Initialize the hardware UART channel
        self.uart = UART(uart_id, baudrate=baudrate, tx=tx_pin, rx=rx_pin, timeout=10)
        
        # Core thread-safety assets
        self.lock = _thread.allocate_lock()
        self.rx_queue = []
        self.buffer = b""
        
        # Start the background data worker thread
        _thread.stack_size(4096) # Cap the stack size for the UART listener
        _thread.start_new_thread(self._listener_worker, ())
        _thread.stack_size(0) 
        
        print(f"[UART] Thread initialized on UART{uart_id} (TX:{tx_pin}, RX:{rx_pin})")

    def _listener_worker(self):
        """Asynchronous internal loop parsing incoming stream lines into the queue."""
        while True:
            try:
                if self.uart.any():
                    with self.lock:
                        # Pull all raw bytes waiting in the hardware ring buffer
                        chunk = self.uart.read(self.uart.any())
                        if chunk:
                            self.buffer += chunk
                        
                        # Process complete lines terminated by a newline character
                        while b'\n' in self.buffer:
                            line, self.buffer = self.buffer.split(b'\n', 1)
                            try:
                                decoded_line = line.decode('utf-8').strip()
                                if decoded_line:
                                    self.rx_queue.append(decoded_line)
                            except Exception:
                                pass # Discard corrupt data frames safely
            except Exception as e:
                print("[UART Thread Error]:", e)
                
            time.sleep_ms(20) # Give other background threads breathing room

    def send(self, message):
        """Safely pushes strings across the serial wire from any thread context."""
        if not message.endswith('\n'):
            message += '\n'
        
        with self.lock:
            self.uart.write(message.encode('utf-8'))

    def any(self):
        """Checks if any complete messages are waiting to be read."""
        with self.lock:
            return len(self.rx_queue) > 0

    def read(self):
        """Pulls the oldest unread string from the queue. Returns None if empty."""
        with self.lock:
            if self.rx_queue:
                return self.rx_queue.pop(0)
        return None