# Copyright 2026 Espressif Systems (Shanghai) Co., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import queue
from typing import Any, Dict, List

from PySide6.QtCore import QObject, QTimer, Signal

from esp_viewer.core.adapter import BaseFrontendAdapter
from esp_viewer.core.protocol import CommandType, EventType, Packet


class PySideAdapterSignals(QObject):
    """
    Defines the PySide Signals used to bridge background threads
    and the main UI thread.
    """

    sig_status_changed = Signal(bool)
    sig_log_message = Signal(dict)
    sig_io_data = Signal(dict)
    sig_param_update = Signal(dict)
    sig_profile_update = Signal(dict)
    sig_text_stream = Signal(dict)
    sig_io_log_status = Signal(dict)

    # We batch stream data to avoid UI stuttering with high-frequency events
    sig_stream_data_batch = Signal(list)


class PySideAdapter(BaseFrontendAdapter):
    """
    Adapts the core EspViewerEngine events to a PySide GUI application.
    It manages cross-thread communication using safe PySide Signals and QTimer.
    """

    def __init__(self):
        super().__init__()
        self.signals = PySideAdapterSignals()

        # Buffer for high-frequency stream events
        self._stream_buffer: List[Dict[str, Any]] = []

        # Timer to flush the stream buffer at a regular GUI-friendly interval
        self._flush_timer = QTimer()
        self._flush_timer.timeout.connect(self._flush_streams)
        self._flush_timer.start(16)  # ~60Hz refresh rate

    def on_event_received(self, packet: Packet):
        """
        Receives an event from the Engine (running in the Engine dispatch thread).
        Routes the event to the appropriate PyQt Signal to cross back into the GUI thread.
        """
        if packet.type == EventType.STREAM_DATA:
            # Buffer stream data for batch processing
            self._stream_buffer.append(packet.payload)
        elif packet.type == EventType.STATUS_CHANGED:
            self.signals.sig_status_changed.emit(packet.payload.get("connected", False))
        elif packet.type == EventType.LOG_MSG:
            self.signals.sig_log_message.emit(packet.payload)
        elif packet.type == EventType.IO_DATA:
            self.signals.sig_io_data.emit(packet.payload)
        elif packet.type == EventType.PARAM_UPDATE:
            self.signals.sig_param_update.emit(packet.payload)
        elif packet.type == EventType.PROFILE_UPDATED:
            self.signals.sig_profile_update.emit(packet.payload)
        elif packet.type == EventType.TEXT_STREAM:
            self.signals.sig_text_stream.emit(packet.payload)
        elif packet.type == EventType.IO_LOG_STATUS:
            self.signals.sig_io_log_status.emit(packet.payload)

    def _flush_streams(self):
        """Called within the GUI thread context via QTimer to flush buffered data."""
        if self._stream_buffer:
            batch = self._stream_buffer[:]  # Shallow copy
            self._stream_buffer.clear()
            self.signals.sig_stream_data_batch.emit(batch)

    # --- UI Action Methods ---
    # These methods are called by the GenericWindow to send actions down to the Engine

    def connect_device(self):
        self.engine.send_command(CommandType.CONNECT)

    def disconnect_device(self):
        self.engine.send_command(CommandType.DISCONNECT)

    def set_param(self, pid: str, value: Any):
        self.engine.send_command(CommandType.SET_PARAM, {"id": pid, "value": value})

    def get_param(self, pid: str):
        self.engine.send_command(CommandType.GET_PARAM, {"id": pid})

    def start_stream(self):
        self.engine.send_command(CommandType.START_STREAM)

    def stop_stream(self):
        self.engine.send_command(CommandType.STOP_STREAM)

    def fetch_profile(self):
        self.engine.send_command(CommandType.FETCH_PROFILE)

    def write_data(self, data: str, is_hex: bool):
        self.engine.send_command(
            CommandType.WRITE_DATA, {"data": data, "is_hex": is_hex}
        )

    def set_io_logging(self, enabled: bool, path: str = None):
        self.engine.send_command(
            CommandType.SET_IO_LOGGING, {"enabled": enabled, "path": path}
        )

    def set_device_interface(self, interface):
        """Pass-through to allow dynamic driver injection from UI thread."""
        self.engine.set_device_interface(interface)
