# Copyright (c) DataLab Platform Developers, BSD 3-Clause license, see LICENSE file.

"""
GUI dialog for analyzing signals and calculating full width at Y.
"""

# pylint: disable=invalid-name  # Allows short reference names like x, y, ...

from __future__ import annotations

import warnings
from typing import TYPE_CHECKING

import numpy as np
from guidata.configtools import get_icon
from plotpy.builder import make
from plotpy.plot import PlotDialog
from qtpy import QtCore as QC
from qtpy import QtGui as QG
from qtpy import QtWidgets as QW
from sigima.tools.signal.pulse import full_width_at_y

from datalab.adapters_plotpy import CURVESTYLES, create_adapter_from_object
from datalab.config import _
from datalab.utils.qthelpers import resize_widget_to_parent

if TYPE_CHECKING:
    from plotpy.items import CurveItem, Marker, XRangeSelection
    from qtpy.QtWidgets import QWidget
    from sigima.objects import SignalObj


class SignalDeltaXDialog(PlotDialog):
    """Signal Delta X dialog.

    Args:
        signal: signal object
        parent: parent widget. Defaults to None.
    """

    def __init__(self, signal: SignalObj, parent: QWidget | None = None) -> None:
        self.__curve_styles = CURVESTYLES.style_generator()
        self.__signal = signal
        self.__coords: list[float, float, float, float] | None = None
        self.curve: CurveItem | None = None
        self.hcursor: Marker | None = None
        self.delta_xrange: XRangeSelection | None = None
        self.deltaxlineedit: QW.QLineEdit | None = None
        self.ylineedit: QW.QLineEdit | None = None
        title = _("Select Y value with cursor")
        super().__init__(title=title, edit=True, parent=parent)
        self.setObjectName("SignalCursorDialog")
        if parent is None:
            self.setWindowIcon(get_icon("DataLab.svg"))
        legend = make.legend("TR")
        self.get_plot().add_item(legend)
        self.__setup_dialog()
        resize_widget_to_parent(self, aspect_ratio=1.0)

    def __setup_dialog(self) -> None:
        """Setup dialog box"""
        apply_button = QW.QPushButton(_("Apply"))
        apply_button.setIcon(get_icon("apply.svg"))
        apply_button.setToolTip(_("Apply cursor position"))
        xlabel = QW.QLabel("∆X=")
        ylabel = QW.QLabel("Y=")
        self.deltaxlineedit = QW.QLineEdit()
        self.deltaxlineedit.setReadOnly(True)
        self.deltaxlineedit.setDisabled(True)
        self.ylineedit = QW.QLineEdit()
        self.ylineedit.editingFinished.connect(self.ylineedit_editing_finished)
        y_validator = QG.QDoubleValidator()
        y_validator.setLocale(QC.QLocale("C"))
        self.ylineedit.setValidator(y_validator)
        xygroup = QW.QGroupBox(_("Cursor position"))
        xylayout = QW.QHBoxLayout()
        xylayout.addWidget(xlabel)
        xylayout.addWidget(self.deltaxlineedit)
        xylayout.addWidget(ylabel)
        xylayout.addWidget(self.ylineedit)
        xylayout.addWidget(apply_button)
        vlayout = QW.QVBoxLayout()
        vlayout.addLayout(xylayout)
        self.warning_label = QW.QLabel()
        vlayout.addWidget(self.warning_label)
        apply_button.clicked.connect(self.ylineedit_editing_finished)
        xygroup.setLayout(vlayout)
        self.button_layout.insertWidget(0, xygroup)

        obj = self.__signal
        with CURVESTYLES.alternative(self.__curve_styles):
            self.curve = create_adapter_from_object(obj).make_item()
        plot = self.get_plot()
        plot.set_antialiasing(True)

        self.delta_xrange = make.xrange(0.0, 1.0)
        self.delta_xrange.setVisible(False)
        self.delta_xrange.set_style("roi", "s/readonly")
        self.delta_xrange.set_selectable(False)

        plot.SIG_MARKER_CHANGED.connect(self.cursor_changed)
        self.hcursor = make.hcursor(np.mean(obj.y), "Y = %g")
        for item in (self.curve, self.delta_xrange, self.hcursor):
            plot.add_item(item)
        plot.replot()
        plot.set_active_item(self.hcursor)
        self.cursor_changed(self.hcursor)

    def cursor_changed(self, item: Marker) -> None:
        """Cursor changed"""
        sig = self.__signal
        _x, y = item.get_pos()

        try:
            with warnings.catch_warnings(record=True) as w:
                self.__coords = full_width_at_y(sig.x, sig.y, y)
                if np.nan in self.__coords:
                    raise ValueError("Invalid coordinates")
                delta_str = f"{self.__coords[2] - self.__coords[0]:g}"
                ok = True
                if len(w) > 0:
                    self.warning_label.setText("⚠️ " + str(w[-1].message))
                else:
                    self.warning_label.setText("")
                self.delta_xrange.setVisible(True)
                self.delta_xrange.set_range(self.__coords[0], self.__coords[2])
        except ValueError:
            delta_str = ""
            ok = False
            self.delta_xrange.setVisible(False)

        self.button_box.button(QW.QDialogButtonBox.Ok).setEnabled(ok)
        self.deltaxlineedit.setText(delta_str)
        self.ylineedit.setText(f"{y:g}" if y is not None else "")

    def ylineedit_editing_finished(self) -> None:
        """Y line edit editing finished"""
        try:
            y = float(self.ylineedit.text())
            x, _y = self.hcursor.get_pos()
            self.hcursor.set_pos(x, y)
        except ValueError:
            pass
        plot = self.get_plot()
        plot.replot()

    def get_coords(self) -> tuple[float, float, float, float]:
        """Return coordinates of segment associated to the width at Y"""
        return self.__coords

    def get_y_value(self) -> float:
        """Get cursor y value"""
        _x, y = self.hcursor.get_pos()
        return y
