#!/usr/bin/env python3
"""
I2C Widget (PyQt6)
==================
GUI for I2C bus control with manual I/O and continuous logging.

Features:
- Bus scanning (detect /dev/i2c-*)
- Device scanning (like i2cdetect)
- Manual read/write operations
- Continuous logger (background polling)
- Data monitor (color-coded)
- Buffer status (embedded compact widget)
- Timestamp synchronization ready
- Theme-ready (global theme controlled)

Author: Kynsight
Version: 1.0.0
"""

from PyQt6.QtWidgets import (
    QWidget, QVBoxLayout, QHBoxLayout, QGridLayout,
    QLabel, QComboBox, QPushButton, QLineEdit,
    QGroupBox, QTextEdit, QSpinBox
)
from PyQt6.QtCore import Qt, QTimer, pyqtSignal
from PyQt6.QtGui import QFont

# Import I2C core
from i2c.i2c_kit.i2c_core import *
from buffer_kit.buffer_widget_compact import CompactBufferWidget


class I2CWidget(QWidget):
    """
    I2C bus control widget.
    
    Signals:
        reading_received: Emitted when logger reading received
        connection_changed: Emitted on connect/disconnect
    """
    
    # Qt signals
    reading_received = pyqtSignal(object)  # I2CReading object
    connection_changed = pyqtSignal(bool)  # True=connected
    
    def __init__(self, parent=None):
        super().__init__(parent)
        
        # Handle state
        self.handle = None
        self.is_connected = False
        
        # Device list (from scan)
        self.detected_devices = []
        
        # Reading history
        self.reading_history = []
        self.max_reading_history = 200
        
        # Overflow tracking (for Data Monitor warnings)
        self._last_overflow_count = 0
        
        # Build UI
        self._init_ui()
        self._setup_timers()
    
    def _init_ui(self):
        """Create user interface."""
        layout = QVBoxLayout()
        self.setLayout(layout)
        
        # Configuration section (compact, horizontal)
        config_group = self._create_config_section()
        layout.addWidget(config_group)
        
        # Status + Buffer on same line
        status_buffer_layout = QHBoxLayout()
        
        # Status label (left)
        self.lbl_status = QLabel("Status: Disconnected")
        status_buffer_layout.addWidget(self.lbl_status, stretch=2)
        
        # Buffer status - compact on right
        buffer_group = self._create_buffer_section()
        status_buffer_layout.addWidget(buffer_group, stretch=1)
        
        layout.addLayout(status_buffer_layout)
        
        # Device scanner
        device_group = self._create_device_section()
        layout.addWidget(device_group)
        
        # Data monitor (shows everything: reads, writes, errors, info)
        display_group = self._create_display_section()
        layout.addWidget(display_group)
        
        # Manual I/O panel
        io_group = self._create_io_section()
        layout.addWidget(io_group)
        
        # Logger panel
        logger_group = self._create_logger_section()
        layout.addWidget(logger_group)
        
        self.setMinimumWidth(800)
    
    # =========================================================================
    # UI Creation
    # =========================================================================
    
    def _create_config_section(self):
        """Bus configuration controls - compact horizontal."""
        group = QGroupBox("I2C Configuration")
        layout = QHBoxLayout()
        group.setLayout(layout)
        
        # Bus selection
        layout.addWidget(QLabel("Bus:"))
        self.combo_bus = QComboBox()
        self.combo_bus.setMinimumWidth(80)
        layout.addWidget(self.combo_bus)
        
        # Refresh buses button
        self.btn_refresh_bus = QPushButton("🔄 Scan")
        self.btn_refresh_bus.clicked.connect(self._refresh_buses)
        layout.addWidget(self.btn_refresh_bus)
        
        layout.addSpacing(20)
        
        # Connect/Disconnect
        self.btn_connect = QPushButton("Connect")
        self.btn_connect.clicked.connect(self._on_connect)
        layout.addWidget(self.btn_connect)
        
        self.btn_disconnect = QPushButton("Disconnect")
        self.btn_disconnect.clicked.connect(self._on_disconnect)
        self.btn_disconnect.setEnabled(False)
        layout.addWidget(self.btn_disconnect)
        
        layout.addStretch()
        
        return group
    
    def _create_buffer_section(self):
        """Buffer status display."""
        group = QGroupBox("Buffer Status")
        layout = QVBoxLayout()
        group.setLayout(layout)
        
        # Placeholder (replaced with CompactBufferWidget when connected)
        self.buffer_widget = QLabel("Not connected")
        layout.addWidget(self.buffer_widget)
        
        # Clear buffer button (only visible when connected)
        self.btn_clear_buffer = QPushButton("Clear Buffer")
        self.btn_clear_buffer.clicked.connect(self._on_clear_buffer)
        self.btn_clear_buffer.setEnabled(False)
        self.btn_clear_buffer.setMaximumHeight(25)
        layout.addWidget(self.btn_clear_buffer)
        
        return group
    
    def _create_device_section(self):
        """Device scanner section."""
        group = QGroupBox("Devices")
        layout = QHBoxLayout()
        group.setLayout(layout)
        
        layout.addWidget(QLabel("Found:"))
        
        # Device dropdown (populated after scan)
        self.combo_devices = QComboBox()
        self.combo_devices.setMinimumWidth(150)
        self.combo_devices.currentIndexChanged.connect(self._on_device_selected)
        layout.addWidget(self.combo_devices)
        
        # Scan button
        self.btn_scan_devices = QPushButton("Scan Bus")
        self.btn_scan_devices.clicked.connect(self._scan_devices)
        self.btn_scan_devices.setEnabled(False)
        layout.addWidget(self.btn_scan_devices)
        
        layout.addStretch()
        
        return group
    
    def _create_display_section(self):
        """Data monitor - shows all activity."""
        group = QGroupBox("Data Monitor (Read=Blue, Write=Green, Error=Red, Info=Gray)")
        layout = QVBoxLayout()
        group.setLayout(layout)
        
        # Display area FIRST (takes most vertical space)
        self.text_display = QTextEdit()
        self.text_display.setReadOnly(True)
        self.text_display.setLineWrapMode(QTextEdit.LineWrapMode.WidgetWidth)  # Enable word wrap
        font = QFont("Courier New", 9)
        self.text_display.setFont(font)
        layout.addWidget(self.text_display)
        
        # Controls at BOTTOM (single line, compact)
        ctrl_layout = QHBoxLayout()
        
        ctrl_layout.addWidget(QLabel("History:"))
        self.spin_history = QSpinBox()
        self.spin_history.setRange(10, 500)
        self.spin_history.setValue(200)
        self.spin_history.valueChanged.connect(self._on_history_changed)
        self.spin_history.setMaximumWidth(70)
        ctrl_layout.addWidget(self.spin_history)
        
        self.btn_clear_display = QPushButton("Clear")
        self.btn_clear_display.setMaximumWidth(80)
        self.btn_clear_display.clicked.connect(self._clear_display)
        ctrl_layout.addWidget(self.btn_clear_display)
        
        ctrl_layout.addStretch()
        
        layout.addLayout(ctrl_layout)
        
        return group
    
    def _create_io_section(self):
        """Manual read/write panel."""
        group = QGroupBox("Manual I/O")
        layout = QHBoxLayout()
        group.setLayout(layout)
        
        # Device address
        layout.addWidget(QLabel("Device:"))
        self.edit_io_addr = QLineEdit("40")
        self.edit_io_addr.setMaximumWidth(60)
        layout.addWidget(self.edit_io_addr)
        
        # Register address
        layout.addWidget(QLabel("Reg:"))
        self.edit_io_reg = QLineEdit("0C")
        self.edit_io_reg.setMaximumWidth(60)
        self.edit_io_reg.returnPressed.connect(self._on_read)  # Enter key reads
        layout.addWidget(self.edit_io_reg)
        
        # Length (for read)
        layout.addWidget(QLabel("Length:"))
        self.spin_io_length = QSpinBox()
        self.spin_io_length.setRange(1, 32)
        self.spin_io_length.setValue(2)
        self.spin_io_length.setMaximumWidth(60)
        layout.addWidget(self.spin_io_length)
        
        # Read button
        self.btn_read = QPushButton("Read")
        self.btn_read.clicked.connect(self._on_read)
        self.btn_read.setEnabled(False)
        layout.addWidget(self.btn_read)
        
        # Write data
        layout.addWidget(QLabel("Write:"))
        self.edit_write_data = QLineEdit()
        self.edit_write_data.setPlaceholderText("FF or FF AA BB")
        self.edit_write_data.setMaximumWidth(150)
        self.edit_write_data.returnPressed.connect(self._on_write)  # Enter key writes
        layout.addWidget(self.edit_write_data)
        
        # Write button
        self.btn_write = QPushButton("Write")
        self.btn_write.clicked.connect(self._on_write)
        self.btn_write.setEnabled(False)
        layout.addWidget(self.btn_write)
        
        # Format selector
        self.combo_format = QComboBox()
        self.combo_format.addItems(["Hex"])
        self.combo_format.setMaximumWidth(80)
        layout.addWidget(self.combo_format)
        
        layout.addStretch()
        
        return group
    
    def _create_logger_section(self):
        """Logger controls."""
        group = QGroupBox("Logger (Continuous Polling)")
        layout = QHBoxLayout()
        group.setLayout(layout)
        
        # Device
        layout.addWidget(QLabel("Device:"))
        self.edit_logger_addr = QLineEdit("40")
        self.edit_logger_addr.setMaximumWidth(60)
        layout.addWidget(self.edit_logger_addr)
        
        # Register
        layout.addWidget(QLabel("Reg:"))
        self.edit_logger_reg = QLineEdit("FE")
        self.edit_logger_reg.setMaximumWidth(60)
        layout.addWidget(self.edit_logger_reg)
        
        # Length
        layout.addWidget(QLabel("Len:"))
        self.spin_logger_length = QSpinBox()
        self.spin_logger_length.setRange(1, 32)
        self.spin_logger_length.setValue(2)
        self.spin_logger_length.setMaximumWidth(60)
        layout.addWidget(self.spin_logger_length)
        
        # Polling rate
        layout.addWidget(QLabel("Rate:"))
        self.combo_logger_rate = QComboBox()
        self.combo_logger_rate.addItems([
            "1000 Hz (1ms)",
            "500 Hz (2ms)",
            "200 Hz (5ms)",
            "100 Hz (10ms)",
            "50 Hz (20ms)",
            "20 Hz (50ms)",
            "10 Hz (100ms)",
            "5 Hz (200ms)",
            "1 Hz (1000ms)"
        ])
        self.combo_logger_rate.setCurrentText("100 Hz (10ms)")
        self.combo_logger_rate.setMinimumWidth(120)
        layout.addWidget(self.combo_logger_rate)
        
        # Start/Stop
        self.btn_logger_start = QPushButton("Start Logger")
        self.btn_logger_start.clicked.connect(self._on_logger_start)
        self.btn_logger_start.setEnabled(False)
        layout.addWidget(self.btn_logger_start)
        
        self.btn_logger_stop = QPushButton("Stop Logger")
        self.btn_logger_stop.clicked.connect(self._on_logger_stop)
        self.btn_logger_stop.setEnabled(False)
        layout.addWidget(self.btn_logger_stop)
        
        layout.addStretch()
        
        return group
    
    def _setup_timers(self):
        """Setup update timers."""
        # Buffer update timer
        self.buffer_timer = QTimer()
        self.buffer_timer.timeout.connect(self._update_buffer_display)
        self.buffer_timer.setInterval(100)  # 100ms
    
    # =========================================================================
    # Bus Management
    # =========================================================================
    
    def _refresh_buses(self):
        """Scan for available I2C buses."""
        self.combo_bus.clear()
        
        buses = i2c_scan_buses()
        
        if buses:
            for bus_id in buses:
                self.combo_bus.addItem(f"/dev/i2c-{bus_id}", bus_id)
            self._log_info(f"Found {len(buses)} I2C bus(es): {buses}")
        else:
            self.combo_bus.addItem("No buses found")
            self._log_warning("No I2C buses found")
    
    def _on_connect(self):
        """Connect to I2C bus."""
        if self.combo_bus.count() == 0:
            self._show_error("No bus selected")
            return
        
        bus_id = self.combo_bus.currentData()
        if bus_id is None:
            self._show_error("Invalid bus selection")
            return
        
        self._log_info(f"Connecting to I2C bus {bus_id}...")
        
        # Create I2C handle
        config = I2CConfig(bus_id=bus_id)
        status, self.handle = i2c_create(config)
        
        if status != Status.OK:
            self._show_error(f"Failed to create handle: {status}")
            return
        
        # Open bus
        status = i2c_open(self.handle)
        
        if status == Status.BUS_NOT_FOUND:
            self._show_error(f"Bus /dev/i2c-{bus_id} not found")
            return
        elif status != Status.OK:
            self._show_error(f"Failed to open bus: {status}")
            return
        
        # Update buffer widget
        self._update_buffer_widget()
        
        # Update UI state
        self.is_connected = True
        self._last_overflow_count = 0  # Reset overflow tracking
        self._update_ui_state()
        self.lbl_status.setText(f"Status: ● Connected to bus {bus_id}")
        self.lbl_status.setStyleSheet("color: green;")
        
        self._log_info(f"✓ Connected to I2C bus {bus_id}")
        
        # Start buffer update timer
        self.buffer_timer.start()
        
        # Auto-scan for devices
        self._log_info("Auto-scanning for devices...")
        QTimer.singleShot(100, self._scan_devices)  # Slight delay for UI update
        
        self.connection_changed.emit(True)
    
    def _on_disconnect(self):
        """Disconnect from I2C bus."""
        self._log_info("Disconnecting...")
        
        if not self.handle:
            return
        
        # Stop logger if running
        if self.handle._logger_running:
            i2c_stop_logger(self.handle)
        
        # Close bus
        i2c_close(self.handle)
        
        self.handle = None
        self.is_connected = False
        
        # Stop buffer timer
        self.buffer_timer.stop()
        
        # Update UI
        self._update_ui_state()
        self.lbl_status.setText("Status: Disconnected")
        self.lbl_status.setStyleSheet("")
        
        self._log_info("✓ Disconnected")
        
        # Replace buffer widget with placeholder
        if isinstance(self.buffer_widget, CompactBufferWidget):
            self.buffer_widget.setParent(None)
            self.buffer_widget = QLabel("Not connected")
            status_buffer_layout = self.layout().itemAt(1)
            buffer_group_widget = status_buffer_layout.itemAt(1).widget()
            buffer_group_widget.layout().addWidget(self.buffer_widget)
        
        self.connection_changed.emit(False)
    
    # =========================================================================
    # Device Scanning
    # =========================================================================
    
    def _scan_devices(self):
        """Scan bus for devices."""
        if not self.handle:
            return
        
        self._log_info("Scanning for devices...")
        
        status, devices = i2c_scan_devices(self.handle)
        
        if status != Status.OK:
            self._show_error("Device scan failed")
            return
        
        # Update device list
        self.detected_devices = devices
        self.combo_devices.clear()
        
        if devices:
            for addr in devices:
                self.combo_devices.addItem(f"{addr:02X}", addr)
            
            self._log_info(f"Found {len(devices)} device(s): {[f'{a:02X}' for a in devices]}")
            
            # First device will be auto-selected, triggering _on_device_selected
        else:
            self.combo_devices.addItem("No devices found")
            self._log_warning("No devices found on bus")
    
    def _on_device_selected(self, index):
        """Handle device selection from dropdown."""
        if index < 0:
            return
        
        addr = self.combo_devices.currentData()
        if addr is None:
            return
        
        # Update I/O and Logger device fields
        addr_str = f"{addr:02X}"
        self.edit_io_addr.setText(addr_str)
        self.edit_logger_addr.setText(addr_str)
        
        self._log_info(f"Selected device: {addr_str}")
    
    # =========================================================================
    # Manual I/O
    # =========================================================================
    
    def _parse_device_address(self, text: str) -> Tuple[bool, int, str]:
        """Parse device address (7-bit I2C: 0x03-0x77)."""
        try:
            text = text.strip()
            if text.startswith("0x") or text.startswith("0X"):
                addr = int(text, 16)
            else:
                addr = int(text, 16)  # Assume hex
            
            if addr < 0x03 or addr > 0x77:
                return (False, 0, "Device address out of range (03-77)")
            
            return (True, addr, "")
        except:
            return (False, 0, "Invalid device address format")
    
    def _parse_register_address(self, text: str) -> Tuple[bool, int, str]:
        """Parse register address (8-bit: 0x00-0xFF)."""
        try:
            text = text.strip()
            if text.startswith("0x") or text.startswith("0X"):
                addr = int(text, 16)
            else:
                addr = int(text, 16)  # Assume hex
            
            if addr < 0x00 or addr > 0xFF:
                return (False, 0, "Register address out of range (00-FF)")
            
            return (True, addr, "")
        except:
            return (False, 0, "Invalid register address format")
    
    def _parse_hex_bytes(self, text: str) -> Tuple[bool, bytes, str]:
        """Parse hex bytes (space or comma separated)."""
        try:
            text = text.strip().replace(",", " ")
            tokens = text.split()
            
            data = []
            for token in tokens:
                if token.startswith("0x"):
                    data.append(int(token, 16))
                else:
                    data.append(int(token, 16))
            
            if not data:
                return (False, b"", "Empty data")
            
            if len(data) > 32:
                return (False, b"", f"Too many bytes (max 32, got {len(data)})")
            
            return (True, bytes(data), "")
        except:
            return (False, b"", "Invalid hex format (use: FF or FF AA BB)")
    
    def _on_read(self):
        """Read from device."""
        if not self.handle:
            return
        
        # Parse device address
        success, addr, error = self._parse_device_address(self.edit_io_addr.text())
        if not success:
            self._show_error(error)
            return
        
        # Parse register address
        success, reg, error = self._parse_register_address(self.edit_io_reg.text())
        if not success:
            self._show_error(f"Register: {error}")
            return
        
        length = self.spin_io_length.value()
        
        # Log TX
        self._log_info(f"Reading: Addr={addr:02X} Reg={reg:02X} Len={length}")
        
        # Read
        if length == 1:
            status, value = i2c_read_byte(self.handle, addr, reg)
            if status == Status.OK:
                data = bytes([value])
            else:
                data = b""
        else:
            status, data = i2c_read_block(self.handle, addr, reg, length)
        
        # Handle result
        if status == Status.OK:
            hex_str = " ".join(f"{b:02X}" for b in data)
            self._log_rx(data, f"{addr:02X}:{reg:02X}")
            self.lbl_status.setText(f"✓ Read {len(data)} bytes")
            self.lbl_status.setStyleSheet("color: green;")
        elif status == Status.NACK:
            self._show_error(f"Device {addr:02X} did not respond (NACK)")
        else:
            self._show_error(f"Read failed: {status}")
    
    def _on_write(self):
        """Write to device."""
        if not self.handle:
            return
        
        # Parse device address
        success, addr, error = self._parse_device_address(self.edit_io_addr.text())
        if not success:
            self._show_error(error)
            return
        
        # Parse register address
        success, reg, error = self._parse_register_address(self.edit_io_reg.text())
        if not success:
            self._show_error(f"Register: {error}")
            return
        
        # Parse data
        success, data, error = self._parse_hex_bytes(self.edit_write_data.text())
        if not success:
            self._show_error(error)
            return
        
        # Log TX
        self._log_tx(data, f"{addr:02X}:{reg:02X}")
        
        # Write
        if len(data) == 1:
            status = i2c_write_byte(self.handle, addr, reg, data[0])
        else:
            status = i2c_write_block(self.handle, addr, reg, data)
        
        # Handle result
        if status == Status.OK:
            self.edit_write_data.clear()
            self.lbl_status.setText(f"✓ Wrote {len(data)} bytes")
            self.lbl_status.setStyleSheet("color: green;")
            self._log_info(f"Write successful: {addr:02X}:{reg:02X}")
        elif status == Status.NACK:
            self._show_error(f"Device {addr:02X} did not respond (NACK)")
        else:
            self._show_error(f"Write failed: {status}")
    
    # =========================================================================
    # Logger
    # =========================================================================
    
    def _parse_logger_rate(self, text: str) -> int:
        """Parse logger rate string to interval_ms."""
        # "100 Hz (10ms)" -> 10
        try:
            parts = text.split("(")[1].split(")")[0]  # Extract "(10ms)"
            return int(parts.replace("ms", ""))
        except:
            return 10  # Default 10ms
    
    def _on_logger_start(self):
        """Start logger."""
        if not self.handle:
            return
        
        # Parse device address
        success, addr, error = self._parse_device_address(self.edit_logger_addr.text())
        if not success:
            self._show_error(error)
            return
        
        # Parse register address
        success, reg, error = self._parse_register_address(self.edit_logger_reg.text())
        if not success:
            self._show_error(f"Register: {error}")
            return
        
        length = self.spin_logger_length.value()
        interval_ms = self._parse_logger_rate(self.combo_logger_rate.currentText())
        
        # Start logger
        status = i2c_start_logger(self.handle, addr, reg, length, interval_ms)
        
        if status == Status.OK:
            self._log_info(f"✓ Logger started: {addr:02X}:{reg:02X} [{length}B] @ {1000/interval_ms:.0f}Hz")
            self.lbl_status.setText("Logger running")
            self.lbl_status.setStyleSheet("color: blue;")
            self._update_ui_state()
        elif status == Status.LOGGER_RUNNING:
            self._show_error("Logger already running")
        else:
            self._show_error(f"Failed to start logger: {status}")
    
    def _on_logger_stop(self):
        """Stop logger."""
        if not self.handle:
            return
        
        i2c_stop_logger(self.handle)
        
        self._log_info("Logger stopped")
        self.lbl_status.setText("Logger stopped")
        self.lbl_status.setStyleSheet("")
        self._update_ui_state()
    
    # =========================================================================
    # UI State Management
    # =========================================================================
    
    def _update_ui_state(self):
        """Enable/disable controls based on state."""
        connected = self.is_connected
        logger_running = self.handle._logger_running if self.handle else False
        
        # Connection controls
        self.combo_bus.setEnabled(not connected)
        self.btn_refresh_bus.setEnabled(not connected)
        self.btn_connect.setEnabled(not connected)
        self.btn_disconnect.setEnabled(connected)
        
        # Device scan
        self.btn_scan_devices.setEnabled(connected and not logger_running)
        
        # Manual I/O
        self.btn_read.setEnabled(connected and not logger_running)
        self.btn_write.setEnabled(connected and not logger_running)
        
        # Logger
        self.btn_logger_start.setEnabled(connected and not logger_running)
        self.btn_logger_stop.setEnabled(connected and logger_running)
        
        # Clear buffer button (enabled when connected and logger not running)
        self.btn_clear_buffer.setEnabled(connected and not logger_running)
        
        # Disable logger config when running
        self.edit_logger_addr.setEnabled(not logger_running)
        self.edit_logger_reg.setEnabled(not logger_running)
        self.spin_logger_length.setEnabled(not logger_running)
        self.combo_logger_rate.setEnabled(not logger_running)
    
    def _update_buffer_widget(self):
        """Replace placeholder with actual buffer widget."""
        if not self.handle or not isinstance(self.buffer_widget, QLabel):
            return
        
        # Remove old label
        self.buffer_widget.setParent(None)
        
        # Create buffer widget
        self.buffer_widget = CompactBufferWidget(self.handle._buffer)
        
        # Add to layout
        status_buffer_layout = self.layout().itemAt(1)
        buffer_group_widget = status_buffer_layout.itemAt(1).widget()
        buffer_group_widget.layout().addWidget(self.buffer_widget)
    
    def _update_buffer_display(self):
        """Update buffer widget periodically and check for overflows."""
        if isinstance(self.buffer_widget, CompactBufferWidget):
            # Get current overflow count
            from buffer_kit.circular_buffer import cb_overflows
            overflow_count = cb_overflows(self.handle._buffer)
            
            # Check if overflow increased
            if overflow_count > self._last_overflow_count:
                overflow_delta = overflow_count - self._last_overflow_count
                # Display RED warning in Data Monitor
                overflow_warning = (
                    f"<span style='color: red; font-weight: bold;'>"
                    f"[WARNING] ⚠ BUFFER OVERFLOW! Lost data. "
                    f"Overflow count: {overflow_count} (+{overflow_delta})"
                    f"</span>"
                )
                self.text_display.append(overflow_warning)
                self._last_overflow_count = overflow_count
            
            # Update buffer widget display
            self.buffer_widget.update_display()
    
    # =========================================================================
    # Display
    # =========================================================================
    
    def _clear_display(self):
        """Clear data monitor."""
        self.text_display.clear()
        self.reading_history.clear()
    
    def _on_history_changed(self, value):
        """Update maximum reading history size."""
        self.max_reading_history = value
    
    def _on_clear_buffer(self):
        """Clear the circular buffer and reset overflow counter."""
        if not self.handle or not self.handle._buffer:
            return
        
        from buffer_kit.circular_buffer import cb_reset
        
        # Reset buffer (clears data and overflow count)
        cb_reset(self.handle._buffer)
        
        # Reset our overflow tracking
        self._last_overflow_count = 0
        
        # Update buffer display
        if isinstance(self.buffer_widget, CompactBufferWidget):
            self.buffer_widget.update_display()
        
        # Log action
        self._log_info("Buffer cleared (data and overflow count reset)")
        self.lbl_status.setText("✓ Buffer cleared")
        self.lbl_status.setStyleSheet("color: green;")
    
    def _log_info(self, message: str):
        """Log info message (gray)."""
        self.text_display.append(f"<span style='color: gray;'>[INFO] {message}</span>")
    
    def _log_warning(self, message: str):
        """Log warning (orange)."""
        self.text_display.append(f"<span style='color: orange;'>[WARNING] {message}</span>")
    
    def _show_error(self, message: str):
        """Show error (red)."""
        self.lbl_status.setText(f"Error: {message}")
        self.lbl_status.setStyleSheet("color: red;")
        self.text_display.append(f"<span style='color: red;'>[ERROR] {message}</span>")
    
    def _log_tx(self, data: bytes, info: str = ""):
        """Log write operation (green) - full data, no truncation."""
        hex_str = " ".join(f"{b:02X}" for b in data)
        prefix = f"[WRITE] {info} " if info else "[WRITE] "
        self.text_display.append(f"<span style='color: green;'>{prefix}{len(data)}B: {hex_str}</span>")
    
    def _log_rx(self, data: bytes, info: str = ""):
        """Log read operation (blue) - full data, no truncation."""
        hex_str = " ".join(f"{b:02X}" for b in data)
        prefix = f"[READ] {info} " if info else "[READ] "
        self.text_display.append(f"<span style='color: blue;'>{prefix}{len(data)}B: {hex_str}</span>")
    
    # =========================================================================
    # Public API
    # =========================================================================
    
    def get_handle(self):
        """Get current I2C handle."""
        return self.handle
    
    def is_bus_connected(self) -> bool:
        """Check if bus is connected."""
        return self.is_connected


# =============================================================================
# Demo
# =============================================================================

if __name__ == "__main__":
    import sys
    from PyQt6.QtWidgets import QApplication
    
    app = QApplication(sys.argv)
    
    widget = I2CWidget()
    widget.setWindowTitle("I2C Control")
    widget.show()
    
    # Auto-refresh buses on startup
    widget._refresh_buses()
    
    sys.exit(app.exec())
