# 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.

from PySide6.QtCore import Qt, Signal
from PySide6.QtWidgets import (QCheckBox, QComboBox, QDoubleSpinBox, QGroupBox,
                               QHBoxLayout, QLabel, QScrollArea, QSlider,
                               QSpinBox, QVBoxLayout, QWidget)


class DynamicControlPanel(QWidget):
    """
    Generates UI widgets from a list of parameter definitions.
    Emits signal (param_id, value) when user changes a widget.
    """

    param_changed = Signal(str, object)

    def __init__(self, parameters, parent=None):
        super().__init__(parent)
        self.parameters = parameters
        self.widgets = {}  # id -> widget
        self._init_ui()

    def _init_ui(self):
        main_layout = QVBoxLayout(self)
        self.group_widgets = {}

        # Scroll Area for many params
        scroll = QScrollArea()
        scroll.setWidgetResizable(True)
        container = QWidget()
        scroll_layout = QVBoxLayout()
        container.setLayout(scroll_layout)

        # Group parameters by 'group' field
        groups = {}
        for param in self.parameters:
            group_name = param.get("group", "General")
            if group_name not in groups:
                groups[group_name] = []
            groups[group_name].append(param)

        # Create widgets for each group
        for group_name, params in groups.items():
            group_box = QGroupBox(group_name)
            group_layout = QVBoxLayout(group_box)

            for param in params:
                widget = self._create_widget(param)
                if widget:
                    # Add label
                    label = QLabel(param.get("label", param["id"]))
                    row = QHBoxLayout()
                    row.addWidget(label)
                    row.addWidget(widget)
                    group_layout.addLayout(row)

            scroll_layout.addWidget(group_box)
            self.group_widgets[group_name] = group_box

        scroll_layout.addStretch()
        scroll.setWidget(container)
        main_layout.addWidget(scroll)

    def _create_widget(self, param):
        w_type = param.get("widget", "spinbox")
        p_type = param.get("type", "int")
        p_id = param["id"]
        default_val = param.get("default")
        widget = None

        if w_type == "slider":
            widget = QSlider(Qt.Horizontal)
            widget.setRange(param.get("min", 0), param.get("max", 100))
            if default_val is not None:
                widget.setValue(int(default_val))
            widget.valueChanged.connect(lambda v, pid=p_id: self._emit_change(pid, v))

        elif w_type == "spinbox":
            if p_type == "float":
                widget = QDoubleSpinBox()
                widget.setDecimals(2)
            else:
                widget = QSpinBox()
            widget.setRange(param.get("min", 0), param.get("max", 100))
            if default_val is not None:
                widget.setValue(default_val)
            widget.valueChanged.connect(lambda v, pid=p_id: self._emit_change(pid, v))

        elif w_type == "combo":
            widget = QComboBox()
            # Expecting options as dict {"0": "Opt A", "1": "Opt B"}
            options = param.get("options", {})
            for key, val in options.items():
                widget.addItem(val, key)  # Data is the key (value to send)

            if default_val is not None:
                idx = widget.findData(str(default_val))
                if idx >= 0:
                    widget.setCurrentIndex(idx)

            widget.currentIndexChanged.connect(
                lambda idx, w=widget, pid=p_id: self._emit_change(pid, w.itemData(idx))
            )

        # Store reference
        if widget:
            self.widgets[p_id] = widget

        return widget

    def _emit_change(self, param_id, value):
        self.param_changed.emit(param_id, value)

    def update_param_value(self, param_id, value):
        """Programmatically update the widget value without emitting signal."""
        if param_id in self.widgets:
            widget = self.widgets[param_id]
            # Block signals to avoid loop
            widget.blockSignals(True)
            try:
                if isinstance(widget, (QSpinBox, QDoubleSpinBox, QSlider)):
                    widget.setValue(value)
                elif isinstance(widget, QComboBox):
                    # Find index for data
                    idx = widget.findData(
                        str(value)
                    )  # Assuming data is stored as string/key
                    if idx >= 0:
                        widget.setCurrentIndex(idx)
            finally:
                widget.blockSignals(False)

    def filter_by_group(self, target_group, keep_others=None):
        """
        Show only groups that match target_group or start with target_group,
        or are in keep_others list.
        """
        if keep_others is None:
            keep_others = []

        for name, widget in self.group_widgets.items():
            visible = False
            if name == target_group or name.startswith(target_group):
                visible = True
            elif name in keep_others:
                visible = True

            # Special case: "Channel X" should match "Channel X Advanced" etc?
            # For now exact match plus startsWith is good.

            widget.setVisible(visible)
