# Source code for taurus.qt.qtgui.display.taurusled

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    # ###########################################################################
    #
    # This file is part of Taurus
    #
    # http://taurus-scada.org
    #
    # Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
    #
    # Taurus is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Lesser General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # Taurus is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Lesser General Public License for more details.
    #
    # You should have received a copy of the GNU Lesser General Public License
    # along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
    #
    # ###########################################################################

    """This module provides a set of basic Taurus widgets based on QLed
    """

    import weakref
    from collections.abc import Sequence

    from taurus.external.qt import Qt

    from taurus.core import DataFormat, AttrQuality, DataType
    from taurus.core.taurusbasetypes import TaurusEventType

    from taurus.qt.qtgui.base import TaurusBaseWidget
    from .qled import QLed

    __docformat__ = "restructuredtext"

    _QT_PLUGIN_INFO = {
        "module": "taurus.qt.qtgui.display",
        "group": "Taurus Display",
        "icon": "designer:ledgreen.png",
    }

    class _TaurusLedController(object):

        #            key     status,     color, inTrouble
        LedMap = {
            True: (True, "green", False),
            False: (False, "black", False),
            None: (False, "black", True),
        }

        LedQualityMap = {
            AttrQuality.ATTR_ALARM: (True, "orange", False),
            AttrQuality.ATTR_CHANGING: (True, "blue", False),
            AttrQuality.ATTR_INVALID: (True, "red", False),
            AttrQuality.ATTR_VALID: (True, "green", False),
            AttrQuality.ATTR_WARNING: (True, "orange", False),
            None: (False, "black", True),
        }

        def __init__(self, widget):
            self._widget = weakref.ref(widget)

        def widget(self):
            return self._widget()

        def modelObj(self):
            return self.widget().getModelObj()

        def value(self):
            widget, obj = self.widget(), self.modelObj()
            fgRole = widget.fgRole
            value = None
            if fgRole == "rvalue":
                value = obj.rvalue
            elif fgRole == "wvalue":
                value = obj.wvalue
            elif fgRole == "quality":
                return obj.quality

            # handle 1D and 2D values
            if obj.data_format is not DataFormat._0D:
                idx = widget.getModelIndexValue()
                if idx:
                    for i in idx:
                        value = value[i]

            return value if value is None else bool(value)

        def usePreferedColor(self, widget):
            return True

        def handleEvent(self, evt_src, evt_type, evt_value):
            self.update()

        def update(self):
            widget = self.widget()

            self._updateDisplay(widget)
            self._updateToolTip(widget)

        def _updateDisplay(self, widget):
            key = None
            try:
                key = self.value()
            except Exception:
                pass
            ledMap = self.LedMap
            if widget.fgRole == "quality":
                ledMap = self.LedQualityMap
            try:
                status, color, trouble = ledMap[key]
            except Exception:
                status, color, trouble = False, "red", True
            if self.usePreferedColor(widget) and not trouble:
                if status:
                    color = widget.onColor
                else:
                    color = widget.offColor
            widget.ledStatus = status
            widget.ledColor = color
            if trouble:
                widget.setAutoFillBackground(True)
                bg_brush = Qt.QBrush(Qt.Qt.BDiagPattern)
                palette = widget.palette()
                palette.setBrush(Qt.QPalette.Window, bg_brush)
                palette.setBrush(Qt.QPalette.Base, bg_brush)
                widget.setPalette(palette)
            else:
                widget.setAutoFillBackground(False)

        def _updateToolTip(self, widget):
            widget.setToolTip(widget.getFormatedToolTip())

    class _TaurusLedControllerBool(_TaurusLedController):
        def usePreferedColor(self, widget):
            # use prefered widget color if representing the boolean read or write
            # value. If representing the quality, use the quality map
            return widget.fgRole != "quality"

    try:
        from taurus.core.tango import DevState  # TODO: Tango-centric

        class _TaurusLedControllerState(_TaurusLedController):

            #                key      status,       color, inTrouble
            LedMap = {
                DevState.ON: (True, "green", False),
                DevState.OFF: (False, "black", False),
                DevState.CLOSE: (True, "white", False),
                DevState.OPEN: (True, "green", False),
                DevState.INSERT: (True, "white", False),
                DevState.EXTRACT: (True, "green", False),
                DevState.MOVING: (True, "blue", False),
                DevState.STANDBY: (True, "yellow", False),
                DevState.FAULT: (True, "red", False),
                DevState.INIT: (True, "yellow", False),
                DevState.RUNNING: (True, "blue", False),
                DevState.ALARM: (True, "orange", False),
                DevState.DISABLE: (True, "magenta", False),
                DevState.UNKNOWN: (False, "black", False),
                None: (False, "black", True),
            }

            def value(self):
                widget, obj = self.widget(), self.modelObj()
                fgRole = widget.fgRole
                value = None
                if fgRole == "rvalue":
                    value = obj.rvalue
                elif fgRole == "wvalue":
                    value = obj.wvalue
                elif fgRole == "quality":
                    value = obj.quality
                return value

            def usePreferedColor(self, widget):
                # never use prefered widget color. Use always the map
                return False

    except Exception:
        pass

    class _TaurusLedControllerDesignMode(_TaurusLedController):
        def _updateDisplay(self, widget):
            widget.ledStatus = True
            if widget.ledStatus:
                widget.ledColor = widget.onColor
            else:
                widget.ledColor = widget.offColor
            widget.setAutoFillBackground(False)

        def _updateToolTip(self, widget):
            widget.setToolTip("Design mode TaurusLed")

    class TaurusLed(QLed, TaurusBaseWidget):
        """A widget designed to represent with a LED image the state of a device,
        the value of a boolean attribute or the quality of an attribute.
        """

        DefaultModelIndex = None
        DefaultFgRole = "rvalue"
        DefaultOnColor = "green"
        DefaultOffColor = "green"

        _deprecatedRoles = dict(value="rvalue", w_value="wvalue")

        def __init__(self, parent=None, designMode=False):
            name = self.__class__.__name__
            self._designMode = designMode
            self._modelIndex = self.DefaultModelIndex
            self._modelIndexStr = ""
            self._fgRole = self.DefaultFgRole
            self._onColor = self.DefaultOnColor
            self._offColor = self.DefaultOffColor
            self._controller = None
            self.call__init__wo_kw(QLed, parent)
            self.call__init__(TaurusBaseWidget, name, designMode=designMode)

            self.controller().update()

        def _calculate_controller_class(self):
            model = self.getModelObj()

            klass = _TaurusLedController
            if self._designMode:
                klass = _TaurusLedControllerDesignMode
            elif model is None:
                klass = _TaurusLedController
            elif model.isBoolean():
                klass = _TaurusLedControllerBool
            elif model.type == DataType.DevState:
                klass = _TaurusLedControllerState  # TODO: tango-centric
            return klass

        def controller(self):
            ctrl = self._controller
            # if there is a controller object and it is not the base controller...
            if ctrl is not None and not ctrl.__class__ == _TaurusLedController:
                return ctrl

            # if there is a controller object and it is still the same class...
            ctrl_klass = self._calculate_controller_class()
            if ctrl is not None and ctrl.__class__ == ctrl_klass:
                return ctrl

            self._controller = ctrl = ctrl_klass(self)
            return ctrl

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # TaurusBaseWidget overwriting
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def handleEvent(self, evt_src, evt_type, evt_value):
            if evt_type in (TaurusEventType.Change, TaurusEventType.Periodic):
                self.emitValueChanged()
            self.controller().handleEvent(evt_src, evt_type, evt_value)

        def isReadOnly(self):
            return True

        def setModel(self, m, **kwargs):
            # force to build another controller
            self._controller = None
            TaurusBaseWidget.setModel(self, m, **kwargs)

        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
        # QT property definition
        # -~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~

        def getFgRole(self):
            return self._fgRole

        def setFgRole(self, fgRole):
            role = self._deprecatedRoles.get(fgRole, fgRole)
            if fgRole != role:
                self.deprecated(
                    rel="4.0",
                    dep="setFgRole(%s)" % fgRole,
                    alt="setFgRole(%s)" % role,
                )
            self._fgRole = str(role)
            self.controller().update()

        def resetFgRole(self):
            self.setFgRole(self.DefaultFgRole)

        def getOnColor(self):
            """Returns the preferred led on color
            :return: led on color
            :rtype: str
            """
            return self._onColor

        def setOnColor(self, color):
            """Sets the preferred led on color

            :param status: the new on color
            :type  status: str
            """
            color = str(color).lower()
            if not self.isLedColorValid(color):
                raise Exception("Invalid color '%s'" % color)
            self._onColor = color
            self.controller().update()

        def resetOnColor(self):
            """Resets the preferred led on color"""
            self.setOnColor(self.DefaultOnColor)

        def getOffColor(self):
            """Returns the preferred led off color
            :return: led off color
            :rtype: str
            """
            return self._offColor

        def setOffColor(self, color):
            """Sets the preferred led off color

            :param status: the new off color
            :type  status: str
            """
            color = str(color).lower()
            if not self.isLedColorValid(color):
                raise Exception("Invalid color '%s'" % color)
            self._offColor = color
            self.controller().update()

        def resetOffColor(self):
            """Resets the preferred led color"""
            self.setOffColor(self.DefaultOffColor)

        def getModelIndexValue(self):
            return self._modelIndex

        def getModelIndex(self):
            return self._modelIndexStr

        def setModelIndex(self, modelIndex):
            mi = str(modelIndex)
            if len(mi) == 0:
                self._modelIndex = None
            else:
                try:
                    mi_value = eval(str(mi))
                except Exception:
                    return
                if type(mi_value) == int:
                    mi_value = (mi_value,)
                if not isinstance(mi_value, Sequence):
                    return
                self._modelIndex = mi_value
            self._modelIndexStr = mi
            self.controller().update()

        def resetModelIndex(self):
            self.setModelIndex(self.DefaultModelIndex)

        @classmethod
        def getQtDesignerPluginInfo(cls):
            d = TaurusBaseWidget.getQtDesignerPluginInfo()
            d.update(_QT_PLUGIN_INFO)
            return d

        #: This property holds the unique URI string representing the model name
        #: with which this widget will get its data from. The convention used for
        #: the string can be found :ref:`here <model-concept>`.
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusBaseWidget.getModel`
        #:     * :meth:`TaurusLabel.setModel`
        #:     * :meth:`TaurusBaseWidget.resetModel`
        #:
        #: .. seealso:: :ref:`model-concept`
        model = Qt.pyqtProperty(
            "QString",
            TaurusBaseWidget.getModel,
            setModel,
            TaurusBaseWidget.resetModel,
        )

        #: (deprecated))
        useParentModel = Qt.pyqtProperty(
            "bool",
            TaurusBaseWidget.getUseParentModel,
            TaurusBaseWidget.setUseParentModel,
            TaurusBaseWidget.resetUseParentModel,
        )

        #: This property holds the index inside the model value that should be
        #: displayed
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusLed.getModelIndex`
        #:     * :meth:`TaurusLed.setModelIndex`
        #:     * :meth:`TaurusLed.resetModelIndex`
        #:
        #: .. seealso:: :ref:`model-concept`
        modelIndex = Qt.pyqtProperty(
            "QString", getModelIndex, setModelIndex, resetModelIndex
        )

        #: This property holds the foreground role.
        #: Valid values are:
        #:
        #:     #. 'value' - the value is used
        #:     #. 'w_value' - the write value is used
        #:     #. 'quality' - the quality is used
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusLed.getFgRole`
        #:     * :meth:`TaurusLed.setFgRole`
        #:     * :meth:`TaurusLed.resetFgRole`
        fgRole = Qt.pyqtProperty(
            "QString", getFgRole, setFgRole, resetFgRole, doc="foreground role"
        )

        #: This property holds the preferred led color
        #: This value is used for the cases where the model value does not contain
        #: enough information to distinguish between different On colors.
        #: For example, a bool attribute, when it is False it is displayed with the
        #: off led but when it is true it may be displayed On in any color. The
        #: prefered color would be used in this case.
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusLed.getOnColor`
        #:     * :meth:`TaurusLed.setOnColor`
        #:     * :meth:`TaurusLed.resetOnColor`
        onColor = Qt.pyqtProperty(
            "QString",
            getOnColor,
            setOnColor,
            resetOnColor,
            doc="preferred led On color",
        )

        #: This property holds the preferred led color
        #: This value is used for the cases where the model value does not contain
        #: enough information to distinguish between different Off colors.
        #: For example, a bool attribute, when it is False it is displayed with the
        #: off led but when it is true it may be displayed On in any color. The
        #: prefered color would be used in this case.
        #:
        #: **Access functions:**
        #:
        #:     * :meth:`TaurusLed.getOffColor`
        #:     * :meth:`TaurusLed.setOffColor`
        #:     * :meth:`TaurusLed.resetOffColor`
        offColor = Qt.pyqtProperty(
            "QString",
            getOffColor,
            setOffColor,
            resetOffColor,
            doc="preferred led Off color",
        )

    def demo():
        "Led"
        from .demo import taurusleddemo

        return taurusleddemo.main()

    def main():
        import sys
        import taurus.qt.qtgui.application

        Application = taurus.qt.qtgui.application.TaurusApplication

        app = Application.instance()
        owns_app = app is None

        if owns_app:
            import taurus.core.util.argparse

            parser = taurus.core.util.argparse.get_taurus_parser()
            parser.usage = "%prog [options] <full_attribute_name(s)>"
            app = Application(
                sys.argv,
                cmd_line_parser=parser,
                app_name="Taurus led demo",
                app_version="1.0",
                org_domain="Taurus",
                org_name="Tango community",
            )

        args = app.get_command_line_args()

        if len(args) == 0:
            w = demo()
        else:
            models = map(str.lower, args)

            w = Qt.QWidget()
            layout = Qt.QGridLayout()
            w.setLayout(layout)
            for model in models:
                led = TaurusLed()
                led.model = model
                layout.addWidget(led)
        w.show()

        if owns_app:
            sys.exit(app.exec_())
        else:
            return w

    if __name__ == "__main__":
        main()