from __future__ import annotations

import logging

import numpy as np
from silx.gui import qt
from silx.gui.colors import Colormap
from silx.io.utils import DataUrl

from ewoks3dxrd.nexus.peaks import read_peaks_attributes
from ewoks3dxrd.nexus.utils import group_exists
from ewoks3dxrd.tasks.detector_spatial_correction import DetectorSpatialCorrection

from .common.ewoks3dxrd_plot2d import Ewoks3DXRDPlot2D
from .common.ewoks3dxrd_widget import Ewoks3DXRDWidget
from .common.execution_control_group_box import ExecutionControlGroupBox
from .detector.detector_control_box import DetectorControlBox

_logger = logging.getLogger(__name__)


class OWDetectorCorrection(Ewoks3DXRDWidget, ewokstaskclass=DetectorSpatialCorrection):
    name = "Detector Correction"
    description = "Correct spatial deformed detector into plane"
    icon = "icons/distortion_plane.svg"

    _ewoks_inputs_to_hide_from_orange = (
        "overwrite",
        "correction_files",
    )

    def __init__(self):
        super().__init__()

        settingsWidget = qt.QWidget(self)
        settingLayout = qt.QVBoxLayout(settingsWidget)
        self._inputParametersBox = DetectorControlBox(self)
        self._executeBox = ExecutionControlGroupBox(
            buttonText="Execute Detector Correction"
        )
        self._executeBox.sigExecuteRequested.connect(self.execute_with_validation)
        self._executeBox.sigUrlChanged.connect(self._onDataURLEditFinished)

        settingLayout.addWidget(self._inputParametersBox, stretch=1)
        settingLayout.addWidget(self._executeBox)
        self.addControlWidget(settingsWidget)

        self._plot = Ewoks3DXRDPlot2D(self)
        self._logPlotEvents(self._plot)
        self._plot.getColorBarWidget().setVisible(False)
        self.addMainWidget(self._plot)
        self.registerInput(
            "segmented_peaks_url",
            self._executeBox.getInputURL,
            self.setSegmentedDataURL,
        )
        self.registerInput(
            "correction_files",
            self._inputParametersBox.getCorrectionFiles,
            self._inputParametersBox.setCorrectionFiles,
        )
        self.registerInput(
            "overwrite",
            self._executeBox.isOverwriteChecked,
            self._executeBox.setOverwrite,
        )

    def validateInputs(self):
        self._executeBox.validate()
        self._inputParametersBox.validate()

    def handleNewSignals(self):
        segmented_peaks_url = self.get_task_input_value("segmented_peaks_url", None)
        if segmented_peaks_url is not None:
            self._executeBox.setInputURL(segmented_peaks_url)
            self._drawInputSegmentedPeaks()
        correction_files = self.get_task_input_value("correction_files", None)
        if correction_files:
            self._inputParametersBox.setCorrectionFiles(correction_files)

    def _drawInputSegmentedPeaks(self):
        data_url = DataUrl(self._executeBox.getInputURL())
        nexus_file_path = data_url.file_path()
        segmented_data_group_path = data_url.data_path()

        if not group_exists(
            filename=nexus_file_path, data_group_path=segmented_data_group_path
        ):
            _logger.debug(
                f"Provided Segmented Data url path does not exist: {data_url}"
            )
            return

        segmented_3d_peaks = read_peaks_attributes(
            filename=nexus_file_path,
            process_name=segmented_data_group_path,
        )
        scatter = self._plot.addScatter(
            x=segmented_3d_peaks["f_raw"],
            y=segmented_3d_peaks["s_raw"],
            value=np.ones(len(segmented_3d_peaks["f_raw"])),
            colormap=Colormap(
                colors=np.array(
                    [
                        [0.0, 0.0, 1.0],
                    ],
                    dtype=np.float32,
                )
            ),
            symbol="o",
            legend="rawPixels",
        )
        scatter.setSymbolSize(7)
        self._plot.setGraphXLabel("fast column")
        self._plot.setGraphYLabel("slow column")
        self._plot.setGraphTitle("Raw Pixels O (blue)")
        self._plot.resetZoom()

    def handleSuccessfulExecution(self):
        self.setOutputDataUrl(self.get_task_output_value("spatial_corrected_data_url"))
        self.displayOutputDataUrl()

    def displayOutputDataUrl(self):
        url = self.getOutputDataUrl()
        data_url = DataUrl(url)
        nexus_file_path = data_url.file_path()
        detector_data_group_path = data_url.data_path()

        det_corrected_3d_peaks = read_peaks_attributes(
            filename=nexus_file_path,
            process_name=detector_data_group_path,
        )
        scatter = self._plot.addScatter(
            x=det_corrected_3d_peaks["fc"],
            y=det_corrected_3d_peaks["sc"],
            value=np.ones(len(det_corrected_3d_peaks["fc"])),
            colormap=Colormap(
                colors=np.array(
                    [
                        [1.0, 0.0, 0.0],
                    ],
                    dtype=np.float32,
                )
            ),
            symbol="+",
            legend="corrected Pixels",
        )
        scatter.setSymbolSize(7)
        self._plot.setGraphTitle("Raw Pixels O  (blue) vs Corrected Pixels + (red)")
        self._plot.resetZoom()

    def _onDataURLEditFinished(self) -> None:
        self._inPeaksWidgetExecution(
            urlWidget=self._executeBox.inputUrlWidget,
            defaultInputKey="segmented_peaks_url",
            errorTitle="Invalid Incoming Segmented Peaks Data group path",
        )

    def _inPeaksWidgetExecution(
        self,
        urlWidget: qt.QLineEdit,
        defaultInputKey: str,
        errorTitle: str,
    ) -> None:
        dataURL = urlWidget.text()
        input_peaks_url = DataUrl(dataURL)
        data_url_exists = group_exists(
            filename=input_peaks_url.file_path(),
            data_group_path=input_peaks_url.data_path(),
        )
        if data_url_exists is False:
            not_found_exception = FileNotFoundError(
                f"The specified data URL does not exist: '{dataURL}'"
            )
            self.showError(not_found_exception, title=errorTitle)
            return
        self.set_default_input(defaultInputKey, dataURL)
        self._drawInputSegmentedPeaks()

    def setSegmentedDataURL(self, dataURL: str):
        self._executeBox.setInputURL(dataURL)
        self._drawInputSegmentedPeaks()
