#!/usr/bin/env python
'''
@file fake_serial_io_module.py

@brief FakeSerialIoModule: fake implementation of SerialIoModule for testing

This module provides a fake serial I/O module that can be used to replace
SerialIoModule during testing. It uses FakeSerialEntry instead of SerialIoEntry.

Copyright (C) Atmosic 2025
'''
from __future__ import annotations

from typing import Optional
from deviceio.device_io_module import DeviceIoModule
from deviceio.fake_serial.fake_serial_entry import FakeSerialEntry


class FakeSerialIoModule(DeviceIoModule):
    """Fake Serial-specific I/O module that extends DeviceIoModule.
    
    This class provides the same interface as SerialIoModule but uses
    FakeSerialEntry instead of SerialIoEntry for testing purposes.
    
    - Provides open/close operations for fake serial port
    - Exposes fake serial object as property for advanced operations
    - Maintains all DeviceIoModule functionality (internal buffer, observers, etc.)
    - Supports context manager protocol for automatic open/close
    """

    def __init__(
        self,
        port: str,
        baudrate: int = 115200,
        rtscts: bool = False,
        stream_type: str = "ascii",
        timeout: Optional[float] = None,
        read_poll_interval: float = 0.01,
    ) -> None:
        """Initialize FakeSerialIoModule with serial port parameters.
        
        Args:
            port: Serial port name (e.g., 'FAKE_COM', 'FAKE_COM_HCI')
            baudrate: Baud rate for serial communication
            rtscts: Enable RTS/CTS flow control
            stream_type: Stream type ('ascii' or 'hex')
            timeout: Read timeout for serial operations
            read_poll_interval: Polling interval for read operations
        """
        # Create FakeSerialEntry
        self._serial_entry = FakeSerialEntry(
            port=port,
            baudrate=baudrate,
            rtscts=rtscts,
            stream_type=stream_type,
            timeout=timeout
        )
        
        # Initialize parent DeviceIoModule
        super().__init__(self._serial_entry, read_poll_interval)
        
        # Store serial parameters for reference
        self._port = port
        self._baudrate = baudrate
        self._rtscts = rtscts
        self._stream_type = stream_type
        self._timeout = timeout

    @property
    def pyserial(self) -> Optional[object]:
        """Access to underlying fake serial object.
        
        Returns:
            FakeSerialPort object if open, None if closed.
        """
        return self._serial_entry._ser

    @property
    def port(self) -> str:
        """Get the serial port name."""
        return self._port

    @property
    def baudrate(self) -> int:
        """Get the baud rate."""
        return self._baudrate

    @property
    def is_open(self) -> bool:
        """Check if fake serial port is open."""
        return self._serial_entry.is_open()

    def open(self) -> bool:
        """Open the fake serial port and start receiver.

        Returns:
            True if successfully opened, False otherwise.
        """
        if self._serial_entry.open():
            # Start receiver automatically when port is opened
            self._start_receiver()
            return True
        return False

    def close(self) -> None:
        """Close the fake serial port and stop receiver."""
        # Stop receiver before closing
        self._stop_receiver()
        self._serial_entry.close()

    # Context manager support
    def __enter__(self) -> "FakeSerialIoModule":
        """Enter context manager."""
        if not self.open():
            raise RuntimeError(f"Failed to open fake serial port: {self._port}")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb) -> None:
        """Exit context manager."""
        self.close()


# Function to monkey patch SerialIoModule for testing
def install_fake_serial_io_module():
    """Replace SerialIoModule with FakeSerialIoModule in the deviceio module."""
    import sys
    from deviceio import serial_io_module
    
    # Replace SerialIoModule class with FakeSerialIoModule
    serial_io_module.SerialIoModule = FakeSerialIoModule
    
    # Also replace in sys.modules if the module is already imported
    if 'deviceio.serial_io_module' in sys.modules:
        sys.modules['deviceio.serial_io_module'].SerialIoModule = FakeSerialIoModule
