# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2015-2023 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.

from __future__ import annotations
from typing import Union
from typing import Optional

import logging
import weakref

from silx.gui import qt
from silx.gui import icons
from silx.gui.widgets.FormGridLayout import FormGridLayout
from bliss.flint.model import flint_model
from bliss.flint.model import plot_model
from bliss.flint.model import plot_item_model
from bliss.flint.model import scan_model
from .image_plot import ImagePlotWidget
from .eye_check_box import EyeCheckBox
from .cog_check_box import CogCheckBox
from .icon_widget import IconWidget
from .utils import plot_helper
from .utils import refresh_helper
from silx.gui.plot.PlotWindow import Plot2D
from .image_stages.flat_field_stage import Layer
from .image_stages.flat_field_stage import extract_exposure_time

_logger = logging.getLogger(__name__)


class _DeviceField(qt.QLabel):
    def plotItem(self):
        plotItem = self.__plotItem
        if plotItem is None:
            return None
        return plotItem()

    def setPlotItem(self, plotItem: plot_item_model.ImageItem):
        if plotItem is None:
            self.setText("")
            self.setEnabled(False)
            return

        dataChannel = plotItem.imageChannel()
        if dataChannel is None:
            self.setText("")
            self.setEnabled(False)
            return

        deviceName, _channelName = dataChannel.name().rsplit(":", 1)
        self.setEnabled(True)
        self.setText(deviceName)


class _ImageField(qt.QWidget):
    def __init__(self, parent):
        super(_ImageField, self).__init__(parent=parent)
        self.__plotItem = None

        self.__label = qt.QLabel(self)
        self.__display = EyeCheckBox(self)
        self.__display.toggled.connect(self.__checked)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)
        layout.addStretch(1)
        layout.addWidget(self.__display)

    def plotItem(self):
        plotItem = self.__plotItem
        if plotItem is None:
            return None
        return plotItem()

    def __checked(self, isChecked):
        plotItem = self.plotItem()
        if plotItem is None:
            return
        plotItem.setVisible(isChecked)

    def setPlotItem(self, plotItem: plot_item_model.ImageItem):
        if plotItem is None:
            self.__plotItem = None
            self.__label.setText("")
            self.setEnabled(False)
            return

        self.__plotItem = weakref.ref(plotItem)

        self.__display.setChecked(plotItem.isVisible())

        dataChannel = plotItem.imageChannel()
        if dataChannel is None:
            self.__label.setText("")
            self.setEnabled(False)
            return

        self.setEnabled(True)
        channelName = dataChannel.name()
        self.__label.setText(channelName)


class _ImageSizeField(qt.QWidget):
    def __init__(self, parent):
        super(_ImageSizeField, self).__init__(parent=parent)
        self.__plotItem = None
        self.__scan = None

        self.__label = qt.QLabel(self)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)

    def plotItem(self):
        plotItem = self.__plotItem
        if plotItem is None:
            return None
        return plotItem()

    def setPlotItem(self, plotItem: plot_item_model.ImageItem):
        self.__plotItem = weakref.ref(plotItem)
        self.updateData()

    def scan(self):
        return self.__scan

    def setScan(self, scan: scan_model.Scan):
        self.__scan = scan
        self.updateData()

    def updateData(self):
        scan = self.__scan
        plotItem = self.plotItem()
        if scan is None or plotItem is None:
            self.__label.setText("-")
            return

        imageChannel = plotItem.imageChannel()
        if imageChannel is None:
            self.__label.setText("")
            return

        data = imageChannel.data(self.__scan)
        if data is None:
            self.__label.setText("")
            return

        array = data.array()
        if array is None:
            self.__label.setText("")
            return

        shape = array.shape
        title = " × ".join(reversed([str(x) for x in shape]))
        self.__label.setText(title)


class _ImageDTypeField(qt.QWidget):
    def __init__(self, parent):
        super(_ImageDTypeField, self).__init__(parent=parent)
        self.__plotItem = None
        self.__scan = None

        self.__label = qt.QLabel(self)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)

    def plotItem(self):
        plotItem = self.__plotItem
        if plotItem is None:
            return None
        return plotItem()

    def setPlotItem(self, plotItem: plot_item_model.ImageItem):
        self.__plotItem = weakref.ref(plotItem)
        self.updateData()

    def scan(self):
        return self.__scan

    def setScan(self, scan: scan_model.Scan):
        self.__scan = scan
        self.updateData()

    def updateData(self):
        scan = self.__scan
        plotItem = self.plotItem()
        if scan is None or plotItem is None:
            self.__label.setText("-")
            return

        imageChannel = plotItem.imageChannel()
        if imageChannel is None:
            self.__label.setText("")
            return

        data = imageChannel.data(self.__scan)
        if data is None:
            self.__label.setText("")
            return

        array = data.array()
        if array is None:
            self.__label.setText("")
            return

        dtype = array.dtype
        self.__label.setText(str(dtype))


class _LayerField(qt.QLabel):

    doubleClicked = qt.Signal()

    def __init__(self, parent=None):
        qt.QLabel.__init__(self, parent=parent)
        self.setToolTip("mock")
        self.doubleClicked.connect(self.__doubleClicked)
        self.__layer: Layer = None

    def mouseDoubleClickEvent(self, event: qt.QEvent):
        "Generates the doubleClicked signal"
        if event.button() == qt.Qt.LeftButton:
            self.doubleClicked.emit()
            event.accept()

    def setLayer(self, layer):
        self.__layer = layer
        if layer is None:
            self.setText("No data")
            self.setToolTip("")
        else:
            if layer.scanId:
                text = f"Scan #{layer.scanId}"
            else:
                text = " × ".join(reversed([str(s) for s in layer.array.shape]))
            self.setText(text)

            msg = ""
            if layer.scanId:
                msg += f"<li><b>Scan id</b>: #{layer.scanId}</li>"
            if layer.scanTitle:
                msg += f"<li><b>Scan title</b>: {layer.scanTitle}</li>"
            msg += f"<li><b>Exposure time</b>: {layer.exposureTime}s</li>"
            self.setToolTip(f"<ul>{msg}</ul>")

    def __doubleClicked(self):
        layer = self.__layer
        if layer is None:
            return

        window = Plot2D(parent=self)
        window.addImage(layer.array, legend=layer.kind)
        window.setKeepDataAspectRatio(True)
        window.getIntensityHistogramAction().setVisible(True)
        if self.__layer.scanTitle:
            window.setGraphTitle(self.__layer.scanTitle)
            window.setWindowTitle(self.__layer.scanTitle)
        window.setWindowFlags(qt.Qt.Window)
        window.show()


class _StagePanel(qt.QWidget):
    def __init__(self, parent):
        super(_StagePanel, self).__init__(parent=parent)
        self.__titleName = None
        self.__stage = None
        self.__enable = None

    def setupTitleName(self, name):
        self.__titleName = name

    def createTitle(self, parent):
        widget = qt.QWidget(parent)
        title = qt.QLabel(parent)
        title.setText(self.__titleName)
        enable = CogCheckBox(parent)
        enable.toggled.connect(self.__changeEnabled)
        enable.setToolTip("Enable/disable the processing")
        layout = qt.QHBoxLayout(widget)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(enable)
        layout.addWidget(title)
        layout.addStretch()
        self.__enable = enable
        return widget

    def __changeEnabled(self, enable):
        stage = self.stage()
        if stage:
            stage.setEnabled(enable)

    def stage(self):
        stage = self.__stage
        if stage:
            return stage()
        self.__stage = None
        return None

    def setStage(self, stage):
        prevStage = self.stage()
        if stage is prevStage:
            return
        if prevStage:
            prevStage.configUpdated.disconnect(self._stageConfigUpdated)
            prevStage.sinkResultUpdated.disconnect(self._stageSinkResultUpdated)
        if stage is None:
            self.__stage = None
        else:
            self.__stage = weakref.ref(stage)
            stage.configUpdated.connect(self._stageConfigUpdated)
            stage.sinkResultUpdated.connect(self._stageSinkResultUpdated)
        self._stageConfigUpdated()
        self._stageSinkResultUpdated()

    def _stageConfigUpdated(self):
        stage = self.stage()
        self.__enable.setChecked(stage.isEnabled())

    def _stageSinkResultUpdated(self):
        pass


class _StatisticsStagePanel(_StagePanel):
    def __init__(self, parent):
        super(_StatisticsStagePanel, self).__init__(parent=parent)
        self.setupTitleName("Statistics")

        self.__min = qt.QLabel(self)
        self.__max = qt.QLabel(self)
        self.__mean = qt.QLabel(self)
        self.__std = qt.QLabel(self)

        layout = qt.QFormLayout(self)
        layout.addRow("Min", self.__min)
        layout.addRow("Max", self.__max)
        layout.addRow("Mean", self.__mean)
        layout.addRow("Std", self.__std)
        layout.setContentsMargins(0, 0, 0, 0)
        self._clearDisplay()

    def _clearDisplay(self):
        self.__min.setText("-")
        self.__max.setText("-")
        self.__mean.setText("-")
        self.__std.setText("-")

    def setFocusWidget(self, widget):
        stage = widget.imageProcessing().statisticsStage()
        self.setStage(stage)

    def _stageSinkResultUpdated(self):
        stage = self.stage()
        if stage is None:
            self._clearDisplay()
            return
        if not stage.isEnabled():
            self._clearDisplay()
            return

        def formatter(v):
            import numbers

            if v is None:
                return "-"
            if isinstance(v, numbers.Integral):
                return str(v)
            else:
                return f"{v:.3f}"

        self.__min.setText(formatter(stage.minimum()))
        self.__max.setText(formatter(stage.maximum()))
        self.__mean.setText(formatter(stage.nanmean()))
        self.__std.setText(formatter(stage.nanstd()))


class _FlatFieldStagePanel(_StagePanel):
    def __init__(self, parent):
        super(_FlatFieldStagePanel, self).__init__(parent=parent)
        self.setupTitleName("Flat field")
        self.__flat = _LayerField(self)
        self.__dark = _LayerField(self)
        layout = qt.QFormLayout(self)
        layout.addRow("Flat", self.__flat)
        layout.addRow("Dark", self.__dark)
        layout.setContentsMargins(0, 0, 0, 0)

    def setFocusWidget(self, widget):
        stage = widget.imageProcessing().flatFieldStage()
        self.setStage(stage)

    def _stageConfigUpdated(self):
        super()._stageConfigUpdated()
        stage = self.stage()
        self.__flat.setLayer(stage.flat())
        self.__dark.setLayer(stage.dark())


class _ExposureTimeStagePanel(_StagePanel):
    def __init__(self, parent):
        super(_ExposureTimeStagePanel, self).__init__(parent=parent)
        self.setupTitleName("Exposure time")
        self.__unit = qt.QLabel(self)
        self.__unit.setText("s")
        self.__unit.setToolTip("Exposure time in seconds")
        self.__expo = qt.QLabel(self)
        self.__expo.setText("-")
        layout = qt.QFormLayout(self)
        layout.addRow(self.__expo, self.__unit)
        layout.setContentsMargins(0, 0, 0, 0)

    def setScan(self, scan: scan_model.Scan):
        _layerKind, exposureTime = extract_exposure_time(scan.scanInfo())
        if exposureTime is not None:
            self.__expo.setText(f"{exposureTime:.3f}")
        else:
            self.__expo.setText("-")

    def setFocusWidget(self, widget):
        stage = widget.imageProcessing().exposureTimeStage()
        self.setStage(stage)


class _SaturationStagePanel(_StagePanel):
    def __init__(self, parent):
        super(_SaturationStagePanel, self).__init__(parent=parent)
        self.setupTitleName("Saturation")
        self.__edit = qt.QLineEdit(self)
        self.__edit.returnPressed.connect(self.__returnPressed)
        layout = qt.QVBoxLayout(self)
        layout.addWidget(self.__edit)
        layout.setContentsMargins(0, 0, 0, 0)

    def __returnPressed(self):
        stage = self.stage()
        if stage is None:
            return
        text = self.__edit.text()
        value = None if text == "" else float(text)
        stage.setValue(value)

    def setFocusWidget(self, widget):
        stage = widget.imageProcessing().saturationStage()
        self.setStage(stage)

    def _stageConfigUpdated(self):
        super()._stageConfigUpdated()
        stage = self.stage()
        value = stage.value()
        self.__edit.setText("" if value is None else str(value))


class _MaskField(qt.QWidget):
    def __init__(self, parent):
        super(_MaskField, self).__init__(parent=parent)
        self.__stage = None
        self.__label = qt.QLabel(self)
        self.__display = EyeCheckBox(self)
        self.__display.setToolTip("Display the mask as an overlay")
        self.__display.toggled.connect(self.__checked)

        self.__load = qt.QToolButton(self)
        self.__load.setAutoRaise(True)
        self.__load.setToolTip("Load a mask from a file")
        icon = icons.getQIcon("flint:icons/roi-load")
        self.__load.setIcon(icon)
        self.__load.clicked.connect(self.__loadMask)

        self.__remove = qt.QToolButton(self)
        self.__remove.setAutoRaise(True)
        self.__remove.setToolTip("Remove the actual mask")
        icon = icons.getQIcon("flint:icons/remove-item-small")
        self.__remove.setIcon(icon)
        self.__remove.clicked.connect(self.__removeMask)

        layout = qt.QHBoxLayout(self)
        layout.setSpacing(0)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__label)
        layout.addSpacing(4)
        layout.addStretch(1)
        layout.addWidget(self.__remove)
        layout.addWidget(self.__load)
        layout.addWidget(self.__display)

    def __removeMask(self):
        stage = self.stage()
        stage.setMask(None)

    def __loadMask(self):
        stage = self.stage()
        stage.requestMaskFile()

    def stage(self):
        stage = self.__stage
        if stage:
            return stage()
        self.__stage = None
        return None

    def stageWasUpdated(self, stage):
        self.__stage = weakref.ref(stage)
        self.__updateWidget()

    def __updateWidget(self):
        stage = self.stage()
        mask = stage.mask()
        if mask is not None:
            maskShape = " × ".join(reversed([str(s) for s in mask.shape]))
        self.__label.setText(maskShape if mask is not None else "No data")
        self.__remove.setVisible(stage.mask() is not None)

    def __checked(self, isChecked):
        stage = self.stage()
        stage.setMaskDisplayedAsLayer(isChecked)


class _MaskStagePanel(_StagePanel):
    def __init__(self, parent):
        super(_MaskStagePanel, self).__init__(parent=parent)
        self.setupTitleName("Mask")
        self.__mask = _MaskField(self)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.__mask)

    def setFocusWidget(self, widget):
        stage = widget.imageProcessing().maskStage()
        self.setStage(stage)

    def _stageConfigUpdated(self):
        super()._stageConfigUpdated()
        stage = self.stage()
        self.__mask.stageWasUpdated(stage)


class _RoiField(qt.QLabel):
    def __init__(self, parent):
        super(_RoiField, self).__init__(parent=parent)
        self.__plotItem = None

        self.__label = qt.QLabel(self)
        self.__icon = IconWidget(self)
        self.__display = EyeCheckBox(self)
        self.__display.toggled.connect(self.__checked)
        layout = qt.QHBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)

        self.__icon.setIconSize(16, 16)
        self.__icon.setIconName("flint:icons/device-image-roi")

        layout.addWidget(self.__icon)
        layout.addWidget(self.__label)
        layout.addStretch(1)
        layout.addWidget(self.__display)

    def plotItem(self):
        plotItem = self.__plotItem
        if plotItem is None:
            return None
        return plotItem()

    def __checked(self, isChecked):
        plotItem = self.plotItem()
        if plotItem is None:
            return
        plotItem.setVisible(isChecked)

    def setPlotItem(self, plotItem: plot_item_model.RoiItem):
        if plotItem is None:
            self.__label.setText("")
            self.setEnabled(False)
            return

        roiName = plotItem.name()
        if roiName is None:
            self.__label.setText("")
            self.setEnabled(False)
            return

        self.__plotItem = weakref.ref(plotItem)

        self.__display.setChecked(plotItem.isVisible())

        self.setEnabled(True)
        self.__label.setText(roiName)


class ImagePlotProperty2Widget(qt.QWidget):
    def __init__(self, parent=None):
        super(ImagePlotProperty2Widget, self).__init__(parent=parent)
        self.__scan: Optional[scan_model.Scan] = None
        self.__flintModel: Optional[flint_model.FlintState] = None
        self.__plotModel: Optional[plot_model.Plot] = None

        self.__focusWidget = None

        self.__aggregator = plot_helper.PlotEventAggregator(self)
        self.__refreshManager = refresh_helper.RefreshManager(self)
        # self.__refreshManager.refreshModeChanged.connect(self.__refreshModeChanged)
        self.__refreshManager.setAggregator(self.__aggregator)

        self.__content = self.__createLayout(self)
        toolBar = self.__createToolBar()

        self.setAutoFillBackground(True)
        line = qt.QFrame(self)
        line.setFrameShape(qt.QFrame.HLine)
        line.setFrameShadow(qt.QFrame.Sunken)

        scroll = qt.QScrollArea(self)
        scroll.setBackgroundRole(qt.QPalette.Light)
        scroll.setFrameShape(qt.QFrame.NoFrame)
        scroll.setWidgetResizable(True)
        scroll.setWidget(self.__content)

        layout = qt.QVBoxLayout(self)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        layout.addWidget(toolBar)
        layout.addWidget(line)
        layout.addWidget(scroll)

    def __createLayout(self, parent):
        content = qt.QWidget(parent)
        layout = FormGridLayout(content)
        layout.setColumnStretch(0, 0)
        layout.setColumnStretch(1, 1)

        self.__deviceName = _DeviceField(content)
        self.__channelName = _ImageField(content)
        self.__imageSize = _ImageSizeField(content)
        self.__imageDType = _ImageDTypeField(content)
        self.__imageState = _StatisticsStagePanel(content)
        self.__imageFFC = _FlatFieldStagePanel(content)
        self.__imageExpo = _ExposureTimeStagePanel(content)
        self.__imageMask = _MaskStagePanel(content)
        self.__imageSaturation = _SaturationStagePanel(content)
        self.__rois = qt.QWidget(content)
        roiLayout = qt.QVBoxLayout(self.__rois)
        roiLayout.setContentsMargins(0, 0, 0, 0)

        layout.addRow("Device", self.__deviceName)
        layout.addRow("Channel name", self.__channelName)
        layout.addRow("Image size", self.__imageSize)
        layout.addRow("Image dtype", self.__imageDType)

        self.__roiSeparator = qt.QFrame(self)
        self.__roiSeparator.setFrameShape(qt.QFrame.HLine)
        self.__roiSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__roiSeparator)
        self.__roiTitle = qt.QLabel(self)
        self.__roiTitle.setText("ROIs")
        layout.addRow(self.__roiTitle, self.__rois)

        self.__saturationSeparator = qt.QFrame(self)
        self.__saturationSeparator.setFrameShape(qt.QFrame.HLine)
        self.__saturationSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__saturationSeparator)
        self.__saturationTitle = self.__imageSaturation.createTitle(self)
        layout.addRow(self.__saturationTitle, self.__imageSaturation)
        self.__toggleSaturationAction(False)

        self.__maskSeparator = qt.QFrame(self)
        self.__maskSeparator.setFrameShape(qt.QFrame.HLine)
        self.__maskSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__maskSeparator)
        self.__maskTitle = self.__imageMask.createTitle(self)
        layout.addRow(self.__maskTitle, self.__imageMask)
        self.__toggleMaskAction(False)

        self.__statisticsSeparator = qt.QFrame(self)
        self.__statisticsSeparator.setFrameShape(qt.QFrame.HLine)
        self.__statisticsSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__statisticsSeparator)
        self.__statisticsTitle = self.__imageState.createTitle(self)
        layout.addRow(self.__statisticsTitle, self.__imageState)
        self.__toggleStatisticsAction(False)

        self.__ffcSeparator = qt.QFrame(self)
        self.__ffcSeparator.setFrameShape(qt.QFrame.HLine)
        self.__ffcSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__ffcSeparator)
        self.__ffcTitle = self.__imageFFC.createTitle(self)
        layout.addRow(self.__ffcTitle, self.__imageFFC)
        self.__toggleFlatFieldAction(False)

        self.__expoSeparator = qt.QFrame(self)
        self.__expoSeparator.setFrameShape(qt.QFrame.HLine)
        self.__expoSeparator.setFrameShadow(qt.QFrame.Sunken)
        layout.addRow(self.__expoSeparator)
        self.__expoTitle = self.__imageExpo.createTitle(self)
        layout.addRow(self.__expoTitle, self.__imageExpo)
        self.__toggleExpoTimeAction(False)

        vstretch = qt.QSpacerItem(
            0, 0, qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding
        )
        layout.addItem(vstretch)
        return content

    def __createToolBar(self):
        toolBar = qt.QToolBar(self)
        toolBar.setMovable(False)

        saturationAction = qt.QAction(self)
        saturationAction.setCheckable(True)
        saturationAction.setText("Enable mask stage")
        saturationAction.toggled.connect(self.__toggleSaturationAction)
        icon = icons.getQIcon("flint:icons/data-saturation")
        saturationAction.setIcon(icon)
        toolBar.addAction(saturationAction)
        self.__saturationAction = saturationAction

        maskAction = qt.QAction(self)
        maskAction.setCheckable(True)
        maskAction.setText("Enable mask stage")
        maskAction.toggled.connect(self.__toggleMaskAction)
        icon = icons.getQIcon("flint:icons/data-mask")
        maskAction.setIcon(icon)
        toolBar.addAction(maskAction)
        self.__maskAction = maskAction

        statisticsAction = qt.QAction(self)
        statisticsAction.setCheckable(True)
        statisticsAction.setText("Enable mask stage")
        statisticsAction.toggled.connect(self.__toggleStatisticsAction)
        icon = icons.getQIcon("flint:icons/data-statistics")
        statisticsAction.setIcon(icon)
        toolBar.addAction(statisticsAction)
        self.__statisticsAction = statisticsAction

        flatFieldAction = qt.QAction(self)
        flatFieldAction.setCheckable(True)
        flatFieldAction.setText("Enable flat field stage")
        flatFieldAction.toggled.connect(self.__toggleFlatFieldAction)
        icon = icons.getQIcon("flint:icons/data-flat-field")
        flatFieldAction.setIcon(icon)
        toolBar.addAction(flatFieldAction)
        self.__flatFieldAction = flatFieldAction

        expoAction = qt.QAction(self)
        expoAction.setCheckable(True)
        expoAction.setText("Enable exposure time normalization")
        expoAction.toggled.connect(self.__toggleExpoTimeAction)
        icon = icons.getQIcon("flint:icons/data-expotime")
        expoAction.setIcon(icon)
        toolBar.addAction(expoAction)
        self.__expoAction = expoAction

        return toolBar

    def __toggleFlatFieldAction(self, checked):
        if self.__focusWidget:
            self.__focusWidget.imageProcessing().flatFieldStage().setEnabled(checked)
        self.__imageFFC.setVisible(checked)
        self.__ffcSeparator.setVisible(checked)
        self.__ffcTitle.setVisible(checked)

    def __toggleExpoTimeAction(self, checked):
        if self.__focusWidget:
            self.__focusWidget.imageProcessing().exposureTimeStage().setEnabled(checked)
        self.__imageExpo.setVisible(checked)
        self.__expoSeparator.setVisible(checked)
        self.__expoTitle.setVisible(checked)

    def __toggleMaskAction(self, checked):
        if self.__focusWidget:
            self.__focusWidget.imageProcessing().maskStage().setEnabled(checked)
        self.__imageMask.setVisible(checked)
        self.__maskSeparator.setVisible(checked)
        self.__maskTitle.setVisible(checked)

    def __toggleSaturationAction(self, checked):
        if self.__focusWidget:
            self.__focusWidget.imageProcessing().saturationStage().setEnabled(checked)
        self.__imageSaturation.setVisible(checked)
        self.__saturationSeparator.setVisible(checked)
        self.__saturationTitle.setVisible(checked)

    def __toggleStatisticsAction(self, checked):
        if self.__focusWidget:
            self.__focusWidget.imageProcessing().statisticsStage().setEnabled(checked)
        self.__imageState.setVisible(checked)
        self.__statisticsSeparator.setVisible(checked)
        self.__statisticsTitle.setVisible(checked)

    def setFlintModel(self, flintModel: flint_model.FlintState = None):
        self.__flintModel = flintModel

    def focusWidget(self):
        return self.__focusWidget

    def setFocusWidget(self, widget: ImagePlotWidget):
        if self.__focusWidget is not None:
            widget.plotModelUpdated.disconnect(self.__plotModelUpdated)
            widget.scanModelUpdated.disconnect(self.__currentScanChanged)
        self.__focusWidget = widget
        if self.__focusWidget is not None:
            widget.plotModelUpdated.connect(self.__plotModelUpdated)
            widget.scanModelUpdated.connect(self.__currentScanChanged)
            plotModel = widget.plotModel()
            scanModel = widget.scan()
        else:
            plotModel = None
            scanModel = None
        self.__plotModelUpdated(plotModel)
        self.__currentScanChanged(scanModel)
        self.__imageState.setFocusWidget(widget)
        self.__imageFFC.setFocusWidget(widget)
        self.__imageExpo.setFocusWidget(widget)
        self.__imageMask.setFocusWidget(widget)
        self.__imageSaturation.setFocusWidget(widget)
        if self.__focusWidget is not None:
            processing = widget.imageProcessing()
            self.__flatFieldAction.setChecked(processing.flatFieldStage().isEnabled())
            self.__maskAction.setChecked(processing.maskStage().isEnabled())
            self.__expoAction.setChecked(processing.exposureTimeStage().isEnabled())
            self.__saturationAction.setChecked(processing.saturationStage().isEnabled())
            self.__statisticsAction.setChecked(processing.statisticsStage().isEnabled())

    def __plotModelUpdated(self, plotModel):
        self.setPlotModel(plotModel)

    def setPlotModel(self, plotModel: plot_model.Plot):
        if self.__plotModel is not None:
            self.__plotModel.structureChanged.disconnect(self.__structureChanged)
            self.__plotModel.itemValueChanged.disconnect(self.__itemValueChanged)
        self.__plotModel = plotModel
        if self.__plotModel is not None:
            self.__plotModel.structureChanged.connect(self.__structureChanged)
            self.__plotModel.itemValueChanged.connect(self.__itemValueChanged)
        self.__updateDisplay()

    def __currentScanChanged(self, scanModel):
        self.__setScan(scanModel)

    def __structureChanged(self):
        self.__updateDisplay()

    def __itemValueChanged(
        self, item: plot_model.Item, eventType: plot_model.ChangeEventType
    ):
        pass

    def plotModel(self) -> Union[None, plot_model.Plot]:
        return self.__plotModel

    def __setScan(self, scan: scan_model.Scan = None):
        if self.__scan is scan:
            return
        if self.__scan is not None:
            self.__scan.scanDataUpdated[object].disconnect(
                self.__aggregator.callbackTo(self.__scanDataUpdated)
            )
            self.__scan.scanStarted.disconnect(
                self.__aggregator.callbackTo(self.__scanStarted)
            )
            self.__scan.scanFinished.disconnect(
                self.__aggregator.callbackTo(self.__scanFinished)
            )
        self.__scan = scan
        # As the scan was updated, clear the previous cached events
        self.__aggregator.clear()
        self.__imageSize.setScan(scan)
        self.__imageDType.setScan(scan)
        self.__imageExpo.setScan(scan)
        if self.__scan is not None:
            self.__scan.scanDataUpdated[object].connect(
                self.__aggregator.callbackTo(self.__scanDataUpdated)
            )
            self.__scan.scanStarted.connect(
                self.__aggregator.callbackTo(self.__scanStarted)
            )
            self.__scan.scanFinished.connect(
                self.__aggregator.callbackTo(self.__scanFinished)
            )
        self.__updateDisplay()

    def __scanStarted(self):
        pass

    def __scanFinished(self):
        pass

    def __scanDataUpdated(self, event: scan_model.ScanDataUpdateEvent):
        plotModel = self.__plotModel
        if plotModel is None:
            return
        self.__imageSize.updateData()
        self.__imageDType.updateData()

    def __updateDisplay(self):
        if self.__plotModel is None:
            return

        roisLayout = self.__rois.layout()
        for i in reversed(range(roisLayout.count())):
            roisLayout.itemAt(i).widget().deleteLater()

        countImages = 0
        countRois = 0
        for plotItem in self.__plotModel.items():
            if isinstance(plotItem, plot_item_model.ImageItem):
                countImages += 1
                self.__channelName.setPlotItem(plotItem)
                self.__deviceName.setPlotItem(plotItem)
                self.__imageSize.setPlotItem(plotItem)
                self.__imageDType.setPlotItem(plotItem)

            elif isinstance(plotItem, plot_item_model.RoiItem):
                countRois += 1
                roi = _RoiField(self.__rois)
                roisLayout.addWidget(roi)
                roi.setPlotItem(plotItem)

        self.__rois.setVisible(countRois > 0)
        self.__roiSeparator.setVisible(countRois > 0)
        self.__roiTitle.setVisible(countRois > 0)

        if countImages == 0:
            self.__channelName.setPlotItem(None)
            self.__deviceName.setPlotItem(None)
        elif countImages >= 2:
            _logger.warning(
                "More than one image is provided by this plot. A single image is supported, others will be ignored."
            )
