"""
Button of general usage.
"""

import numpy
import logging
from typing import Optional, Union
from silx.gui import qt
from silx.gui.plot.tools.roi import RegionOfInterestManager
from silx.gui.plot.items.roi import LineROI
from silx.gui.plot.PlotToolButtons import PlotToolButton

from tomwer.gui import icons
from pyunitsystem.metricsystem import MetricSystem


_logger = logging.getLogger(__file__)


class PadlockButton(qt.QPushButton):
    """Simple button to define a button with PadLock icons"""

    sigLockChanged = qt.Signal(bool)
    """signal emitted when the lock status change"""

    def __init__(self, parent):
        qt.QPushButton.__init__(self, parent)
        self._lockIcon = icons.getQIcon("locked")
        self._unlockIcon = icons.getQIcon("unlocked")
        self.setIcon(self._unlockIcon)
        self.setCheckable(True)

        # connect signals
        self.toggled.connect(self._updateDisplay)

    def setLock(self, lock: bool):
        self.setChecked(lock)
        self._updateDisplay(lock)

    def _updateDisplay(self, checked):
        _icon = self._lockIcon if checked else self._unlockIcon
        self.setIcon(_icon)
        self.sigLockChanged.emit(checked)

    def isLocked(self) -> bool:
        return self.isChecked()


class TabBrowsersButtons(qt.QWidget):
    """Simple widget containing buttons to go to 'next' or 'previous'"""

    sigPreviousReleased = qt.Signal()
    """emitted when the previous button is released"""
    sigNextReleased = qt.Signal()
    """emitted when the next button is released"""

    def __init__(self, parent=None):
        qt.QWidget.__init__(self, parent)
        self.setLayout(qt.QHBoxLayout())
        # define gui
        style = qt.QApplication.style()
        self._previousButton = qt.QPushButton("previous", self)
        previous_icon = style.standardIcon(qt.QStyle.SP_ArrowLeft)
        self._previousButton.setIcon(previous_icon)
        self.layout().addWidget(self._previousButton)

        self._nextButton = qt.QPushButton("next", self)
        next_icon = style.standardIcon(qt.QStyle.SP_ArrowRight)
        self._nextButton.setIcon(next_icon)
        self.layout().addWidget(self._nextButton)
        spacer = qt.QWidget(self)
        spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
        self.layout().addWidget(spacer)

        # connect signal / slot
        self._previousButton.released.connect(self._previousReleased)
        self._nextButton.released.connect(self._nextReleased)

    def _previousReleased(self, *args, **kwargs):
        self.sigPreviousReleased.emit()

    def _nextReleased(self, *args, **kwargs):
        self.sigNextReleased.emit()


class TapeMeasureToolButton(PlotToolButton):
    """Button to active measurement between two point of the plot"""

    class TapeMeasureROI(LineROI):
        def __init__(self, parent=None, pixel_size_m=None):
            super().__init__(parent)
            self._pixel_size_m = None
            self.setPixelSize(pixel_size_m)

        def setEndPoints(self, startPoint, endPoint):
            distance_px = numpy.linalg.norm(endPoint - startPoint)
            super().setEndPoints(startPoint=startPoint, endPoint=endPoint)
            if self._pixel_size_m is None:
                self._updateText(f"{distance_px :.1f}px")
            else:
                distance_m = distance_px * self._pixel_size_m
                value, unit = MetricSystem.cast_metric_to_best_unit(distance_m)
                self._updateText(f"{distance_px :.1f}px ({value:.2f}{unit})")

        def setPixelSize(self, pixel_size_m: Optional[Union[tuple, float]]):
            if isinstance(pixel_size_m, (tuple, list)):
                assert (
                    len(pixel_size_m) == 2
                ), "expects at most two pixel size values (x and y values)"
                if not numpy.isclose(pixel_size_m[0], pixel_size_m[1]):
                    value, unit = MetricSystem.cast_metric_to_best_unit(pixel_size_m[0])
                    _logger.warning(
                        f"TapeMeasure is only handling square pixels for now. Will consider the pixel is {value:.2f}{unit}x{value:.2f}{unit}"
                    )
                pixel_size_m = pixel_size_m[0]
            self._pixel_size_m = pixel_size_m

    def __init__(self, parent=None, plot=None, pixel_size_mm=None):
        super().__init__(parent=parent, plot=plot)
        self._roiManager = None
        self._lastRoiCreated = None
        self._pixel_sixel_m = pixel_size_mm
        self.setIcon(icons.getQIcon("ruler"))
        self.setToolTip("measure distance between two pixels")
        self.toggled.connect(self._callback)
        self._connectPlot(plot)

    def setPlot(self, plot):
        return super().setPlot(plot)

    def setPixelSize(self, pixel_size_m: Optional[Union[tuple, float]]):
        self._pixel_sixel_m = pixel_size_m

    def _callback(self, toggled):
        if not self._roiManager:
            return
        if self._lastRoiCreated is not None:
            self._lastRoiCreated.setVisible(self.isChecked())
        if self.isChecked():
            self._roiManager.start(
                self.TapeMeasureROI,
                self,
            )
            self.__interactiveModeStarted(self._roiManager)
        else:
            source = self._roiManager.getInteractionSource()
            if source is self:
                self._roiManager.stop()

    def __interactiveModeStarted(self, roiManager):
        roiManager.sigInteractiveModeFinished.connect(self.__interactiveModeFinished)

    def __interactiveModeFinished(self):
        roiManager = self._roiManager
        if roiManager is not None:
            roiManager.sigInteractiveModeFinished.disconnect(
                self.__interactiveModeFinished
            )
        self.setChecked(False)

    def _connectPlot(self, plot):
        """
        Called when the plot is connected to the widget
        :param plot: :class:`.PlotWidget` instance
        """
        if plot is None:
            return
        self._roiManager = RegionOfInterestManager(plot)
        self._roiManager.setColor("yellow")  # Set the color of ROI
        self._roiManager.sigRoiAdded.connect(self._registerCurrentROI)

    def _disconnectPlot(self, plot):
        if plot and self._lastRoiCreated is not None:
            self._roiManager.removeRoi(self._lastRoiCreated)
            self._lastRoiCreated = None
        return super()._disconnectPlot(plot)

    def _registerCurrentROI(self, currentRoi):
        if self._lastRoiCreated is None:
            self._lastRoiCreated = currentRoi
            self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
        elif currentRoi != self._lastRoiCreated and self._roiManager is not None:
            self._roiManager.removeRoi(self._lastRoiCreated)
            self._lastRoiCreated = currentRoi
            self._lastRoiCreated.setPixelSize(self._pixel_sixel_m)
