#! /usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Copyright 2021, Nils Hilbricht, Germany ( https://www.hilbricht.net )

This file is part of the Laborejo Software Suite ( https://www.laborejo.org ),

Laborejo2 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import logging; logger = logging.getLogger(__name__); logger.info("import")

#Standard Library

#Third party
from PyQt5 import QtCore, QtGui, QtWidgets
translate = QtCore.QCoreApplication.translate

#Template
from template.helper import pairwise

#Our own files
from .constantsAndConfigs import constantsAndConfigs
from .designer.trackWidget import Ui_trackGroupWidget
from .submenus import TickWidget, CombinedTickWidget
from contextlib import contextmanager
import engine.api as api


class TrackWidget(QtWidgets.QGroupBox):
    #TODO: ideas: number of blocks, which CCs are set, review/change durationSettingsSignature, dynamicSettingsSignature
    def __init__(self, parentDataEditor, trackExportObject):
        super().__init__()
        self.parentDataEditor = parentDataEditor
        self.trackExportObject = trackExportObject
        self.ui = Ui_trackGroupWidget()
        self.ui.setupUi(self)

        self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

        self.trackExportObject = trackExportObject #updated on every self.updateData
        self.ui.visibleCheckbox.clicked.connect(self.visibleToggled) #only user changes, not through setChecked()
        self.ui.audibleCheckbox.clicked.connect(self.audibleToggled) #only user changes, not through setChecked()
        self.ui.doubleTrackCheckbox.clicked.connect(self.doubleTrackToggled) #only user changes, not through setChecked()
        #self.ui.upbeatSpinBox.editingFinished.connect(self.upbeatChanged) #only user changes, not through setText() etc.
        self.ui.upbeatSpinBox.valueChanged.connect(self.upbeatChanged) #also through the tickWidget
        self.ui.callTickWidget.clicked.connect(self.callClickWidgetForUpbeat)
        self.ui.nameLineEdit.editingFinished.connect(self.nameChanged)  #only user changes, not through setText() etc.
        self.ui.deleteButton.clicked.connect(lambda: api.deleteTrack(self.trackExportObject["id"]))

        self.ui.midiChannelSpinBox.valueChanged.connect(self.dataChanged)
        self.ui.midiProgramSpinBox.valueChanged.connect(self.dataChanged)
        self.ui.midiBankMsbSpinBox.valueChanged.connect(self.dataChanged)
        self.ui.midiBankLsbSpinBox.valueChanged.connect(self.dataChanged)
        self.ui.midiTransposeSpinBox.valueChanged.connect(self.dataChanged)

        self.ui.instrumentName.editingFinished.connect(self.nameChanged)
        self.ui.shortInstrumentName.editingFinished.connect(self.nameChanged)


        #Create a menu with checkboxes to allow switching on and off of additional channels for the CC sub-track
        #However, we will not use normal checkable Menu actions since they close the menu after triggering. even blockSignals does not prevent closing
        self.ccChannels = {}
        ccChannelMenu = QtWidgets.QMenu()
        for i in range(1,17): #excluding 17
            checkbox = QtWidgets.QCheckBox(ccChannelMenu)
            self.ccChannels[i] = checkbox
            checkbox.stateChanged.connect(self.dataChanged)
            widget = QtWidgets.QWidget()
            layout = QtWidgets.QFormLayout()
            layout.setContentsMargins(0,0,0,0) #left, top, right, bottom in pixel
            widget.setLayout(layout)
            widgetAction = QtWidgets.QWidgetAction(ccChannelMenu)
            widgetAction.setDefaultWidget(widget)
            layout.addRow(str(i).zfill(2), checkbox)
            ccChannelMenu.addAction(widgetAction)

            #action = QtWidgets.QAction(str(i), ccChannelMenu)
            #action.setCheckable(True)
            #ccChannelMenu.addAction(action)
        self.ui.ccChannelsPushButton.setMenu(ccChannelMenu)

        #TODO: The callbacks below trigger quite often. If you move the mouse wheel in a spin box each increase/decrease sends ALL track settings to the backend which triggers a compelte redraw of the track. This should be measure on performance and improved. But even incremental updates should have an immediate effect on a running playback so "send changes when closing the track editor" is only a compromise". Incremental updates are not a a solution either because even those need a playback update. valueChanged does not have an editingFinished signal like text fields.

        #Set up the advanced properties. They should be hidden at the start
        #Tab 1: Durations
        self.ui.defaultOn.editingFinished.connect(self.dataChanged) #only user changes, not through setText() etc.
        self.ui.defaultOff.editingFinished.connect(self.dataChanged)
        self.ui.staccatoOn.editingFinished.connect(self.dataChanged)
        self.ui.staccatoOff.editingFinished.connect(self.dataChanged)
        self.ui.tenutoOn.editingFinished.connect(self.dataChanged)
        self.ui.tenutoOff.editingFinished.connect(self.dataChanged)
        self.ui.legatoOn.editingFinished.connect(self.dataChanged)
        self.ui.legatoOff.editingFinished.connect(self.dataChanged)

        #Tab 2: Dynamics
        self.ui.dynamics_ppppp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_pppp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_ppp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_pp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_p.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_mp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_mf.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_f.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_ff.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_fff.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_ffff.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_custom.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_tacet.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_fp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_sp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_spp.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_sfz.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_sf.valueChanged.connect(self.dataChanged)
        self.ui.dynamics_sff.valueChanged.connect(self.dataChanged)

        self.ui.buttonResetDurations.clicked.connect(lambda: api.resetDuationSettingsSignature(self.trackExportObject["id"]))
        self.ui.buttonResetDynamics.clicked.connect(lambda: api.resetDynamicSettingsSignature(self.trackExportObject["id"]))

        #Connect the hide checkbox
        self.ui.advanced.toggled.connect(self.advancedToggled)
        self.ui.advanced.setChecked(False)

    def visibleToggled(self, signal):
        assert signal == bool(self.ui.visibleCheckbox.checkState())
        if signal:
            api.unhideTrack(self.trackExportObject["id"])
        else:
            api.hideTrack(self.trackExportObject["id"])

    def audibleToggled(self, signal):
        assert signal == bool(self.ui.audibleCheckbox.checkState())
        api.trackAudible(self.trackExportObject["id"], signal)

    def doubleTrackToggled(self, signal):
        api.setDoubleTrack(self.trackExportObject["id"], signal)

    def advancedToggled(self, bool):
        assert bool == self.ui.advanced.isChecked()
        if bool:
            self.ui.advancedContent.show()
        else:
            self.ui.advancedContent.hide()

    @contextmanager
    def blockUiSignals(self):
        """prevent loops by blocking all signals that react on changed states.
        Revoked at the end of the calling function.
        Strictly this is not needed for signals like .clicked() (compared to .toggled()) but it is
        a good last line of defense to prevent future bugs. Those signal-loops are hard to track.

        ADD ALL NEW SUB WIDGETS HERE!!!!! THIS HAPPENED TWICED ALREADY AND WAS A NIGHTMARE TO DEBUG!
        SIMPLY USING self.blockSignals(True) HAS NO EFFECT BECAUSE THOSE ARE IN .ui.
        self.setUpdatesEnabled(False) doesn't work as well.
        self.children() does not return the right children either, self.ui() is a python object,
        not a qt widget."""

        for widget in self.ui.__dict__.values():
            widget.blockSignals(True)
        for checkbox in self.ccChannels.values():
            checkbox.blockSignals(True)
        yield
        for widget in self.ui.__dict__.values():
            widget.blockSignals(False)
        for checkbox in self.ccChannels.values():
            checkbox.blockSignals(False)

    def callClickWidgetForUpbeat(self):
        dialog = TickWidget(self.parentDataEditor, initValue = self.ui.upbeatSpinBox.value())
        self.ui.upbeatSpinBox.setValue(dialog.ui.ticks.value())

    def nameChanged(self):
        """When enter is pressed or focus is lost"""
        t = self.ui.nameLineEdit.text()
        name = self.ui.instrumentName.text()
        short = self.ui.shortInstrumentName.text()
        api.setTrackName(self.trackExportObject["id"], nameString = t, initialInstrumentName = name, initialShortInstrumentName = short)

    def upbeatChanged(self):
        """When enter is pressed or focus is lost"""
        v = self.ui.upbeatSpinBox.value()
        api.setTrackUpbeat(self.trackExportObject["id"], v)

    def dataChanged(self):
        """Our data changed. Send to Engine"""
        with self.blockUiSignals():
            dictionary = {}
            dictionary["initialMidiChannel"] = self.ui.midiChannelSpinBox.value()-1
            dictionary["initialMidiProgram"] = self.ui.midiProgramSpinBox.value()
            dictionary["initialMidiBankMsb"] = self.ui.midiBankMsbSpinBox.value()
            dictionary["initialMidiBankLsb"] = self.ui.midiBankLsbSpinBox.value()
            dictionary["ccChannels"] = tuple(chanNum-1 for chanNum, checkbox in self.ccChannels.items() if checkbox.checkState()) #can be unsorted
            dictionary["midiTranspose"] = self.ui.midiTransposeSpinBox.value()

            dictionary["duration.defaultOn"] = self.ui.defaultOn.text()
            dictionary["duration.defaultOff"] = self.ui.defaultOff.text()
            dictionary["duration.staccatoOn"] = self.ui.staccatoOn.text()
            dictionary["duration.staccatoOff"] = self.ui.staccatoOff.text()
            dictionary["duration.tenutoOn"] = self.ui.tenutoOn.text()
            dictionary["duration.tenutoOff"] = self.ui.tenutoOff.text()
            dictionary["duration.legatoOn"] = self.ui.legatoOn.text()
            dictionary["duration.legatoOff"] = self.ui.legatoOff.text()

            dictionary["dynamics.ppppp"] = self.ui.dynamics_ppppp.value()
            dictionary["dynamics.pppp"] = self.ui.dynamics_pppp.value()
            dictionary["dynamics.ppp"] = self.ui.dynamics_ppp.value()
            dictionary["dynamics.pp"] = self.ui.dynamics_pp.value()
            dictionary["dynamics.p"] = self.ui.dynamics_p.value()
            dictionary["dynamics.mp"] = self.ui.dynamics_mp.value()
            dictionary["dynamics.mf"] = self.ui.dynamics_mf.value()
            dictionary["dynamics.f"] = self.ui.dynamics_f.value()
            dictionary["dynamics.ff"] = self.ui.dynamics_ff.value()
            dictionary["dynamics.fff"] = self.ui.dynamics_fff.value()
            dictionary["dynamics.ffff"] = self.ui.dynamics_ffff.value()
            dictionary["dynamics.custom"] = self.ui.dynamics_custom.value()
            dictionary["dynamics.tacet"] = self.ui.dynamics_tacet.value()
            dictionary["dynamics.fp"] = self.ui.dynamics_fp.value()
            dictionary["dynamics.sp"] = self.ui.dynamics_sp.value()
            dictionary["dynamics.spp"] = self.ui.dynamics_spp.value()
            dictionary["dynamics.sfz"] = self.ui.dynamics_sfz.value()
            dictionary["dynamics.sf"] = self.ui.dynamics_sf.value()
            dictionary["dynamics.sff"] = self.ui.dynamics_sff.value()

            if not self.trackExportObject == dictionary: #checks for keys and values
                api.setTrackSettings(self.trackExportObject["id"], dictionary)
            #else: no change

    def updateData(self, trackExportObject):
        """Receives api updates. Change GUI fields accordingly"""
        self.trackExportObject = trackExportObject

        with self.blockUiSignals():
            self.trackExportObject = trackExportObject
            self.ui.upbeatSpinBox.setValue(trackExportObject["upbeatInTicks"])
            self.ui.nameLineEdit.setText(trackExportObject["name"])
            self.ui.midiChannelSpinBox.setValue(trackExportObject["initialMidiChannel"]+1)
            self.ui.midiProgramSpinBox.setValue(trackExportObject["initialMidiProgram"])
            self.ui.midiBankMsbSpinBox.setValue(trackExportObject["initialMidiBankMsb"])
            self.ui.midiBankLsbSpinBox.setValue(trackExportObject["initialMidiBankLsb"])
            self.ui.midiTransposeSpinBox.setValue(trackExportObject["midiTranspose"])
            self.ui.instrumentName.setText(trackExportObject["initialInstrumentName"])
            self.ui.shortInstrumentName.setText(trackExportObject["initialShortInstrumentName"])

            self.ui.defaultOn.setText(trackExportObject["duration.defaultOn"])
            self.ui.defaultOff.setText(trackExportObject["duration.defaultOff"])
            self.ui.staccatoOn.setText(trackExportObject["duration.staccatoOn"])
            self.ui.staccatoOff.setText(trackExportObject["duration.staccatoOff"])
            self.ui.tenutoOn.setText(trackExportObject["duration.tenutoOn"])
            self.ui.tenutoOff.setText(trackExportObject["duration.tenutoOff"])
            self.ui.legatoOn.setText(trackExportObject["duration.legatoOn"])
            self.ui.legatoOff.setText(trackExportObject["duration.legatoOff"])

            self.ui.dynamics_ppppp.setValue(trackExportObject["dynamics.ppppp"])
            self.ui.dynamics_pppp.setValue(trackExportObject["dynamics.pppp"])
            self.ui.dynamics_ppp.setValue(trackExportObject["dynamics.ppp"])
            self.ui.dynamics_pp.setValue(trackExportObject["dynamics.pp"])
            self.ui.dynamics_p.setValue(trackExportObject["dynamics.p"])
            self.ui.dynamics_mp.setValue(trackExportObject["dynamics.mp"])
            self.ui.dynamics_mf.setValue(trackExportObject["dynamics.mf"])
            self.ui.dynamics_f.setValue(trackExportObject["dynamics.f"])
            self.ui.dynamics_ff.setValue(trackExportObject["dynamics.ff"])
            self.ui.dynamics_fff.setValue(trackExportObject["dynamics.fff"])
            self.ui.dynamics_ffff.setValue(trackExportObject["dynamics.ffff"])
            self.ui.dynamics_custom.setValue(trackExportObject["dynamics.custom"])
            self.ui.dynamics_tacet.setValue(trackExportObject["dynamics.tacet"])
            self.ui.dynamics_fp.setValue(trackExportObject["dynamics.fp"])
            self.ui.dynamics_sp.setValue(trackExportObject["dynamics.sp"])
            self.ui.dynamics_spp.setValue(trackExportObject["dynamics.spp"])
            self.ui.dynamics_sfz.setValue(trackExportObject["dynamics.sfz"])
            self.ui.dynamics_sf.setValue(trackExportObject["dynamics.sf"])
            self.ui.dynamics_sff.setValue(trackExportObject["dynamics.sff"])

            if trackExportObject["initialMidiProgram"] >= 0:
                self.ui.midiBankMsbSpinBox.setEnabled(True)
                self.ui.midiBankLsbSpinBox.setEnabled(True)
            else:
                self.ui.midiBankMsbSpinBox.setEnabled(False)
                self.ui.midiBankLsbSpinBox.setEnabled(False)

            #hidden position can be 0 as well. So don't bool, check for None explicitely.
            if trackExportObject["hiddenPosition"] is None: #visible
                self.ui.visibleCheckbox.setChecked(True)
                self.ui.deleteButton.setEnabled(True)
            else:
                self.ui.visibleCheckbox.setChecked(False)
                self.ui.deleteButton.setEnabled(False) #You cannot delete hidden tracks. see api.deleteTrack

            self.ui.doubleTrackCheckbox.setChecked(trackExportObject["double"])
            self.ui.audibleCheckbox.setChecked(trackExportObject["audible"])

            for i in range(0,16): #without 16
                #Engine from 0 to 15, GUI from 1 to 16.
                self.ccChannels[i+1].setChecked(i in trackExportObject["ccChannels"]) #a checkbox widget.



class TrackEditor(QtWidgets.QWidget):
    """Created by ControlMainWindow. One permanent instance"""
    def __init__(self, mainWindow):
        super().__init__()
        self.mainWindow = mainWindow

        self.layout = QtWidgets.QVBoxLayout()
        self.layout.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        self.setLayout(self.layout)

        api.callbacks.tracksChangedIncludingHidden.append(self.updateTrackWidgets)
        self.tracks = {} #id:trackWidget. As any dict, not in order

        #Upbeat Tick Widget and Action-Button
        allUpbeats = QtWidgets.QWidget()
        allUpbeatsLayout = QtWidgets.QHBoxLayout()
        allUpbeatsLayout.setAlignment(QtCore.Qt.AlignTop | QtCore.Qt.AlignLeft)
        allUpbeats.setLayout(allUpbeatsLayout)
        self.layout.addWidget(allUpbeats)

        self.allUpbeatsCombinedTickWidget = CombinedTickWidget()

        allUpbeatsPushButton = QtWidgets.QPushButton(translate("trackEditorPythonFile", "set all upbeats"))
        allUpbeatsPushButton.clicked.connect(self.setAllUpbeats) #gets the value itself

        allUpbeatsLayout.addWidget(self.allUpbeatsCombinedTickWidget)
        allUpbeatsLayout.addWidget(allUpbeatsPushButton)

        #Reset all Advanced Views
        foldAllAdvanvced = QtWidgets.QPushButton(translate("trackEditorPythonFile", "fold all advanced"))
        foldAllAdvanvced.clicked.connect(self.foldAllAdvanvced)
        allUpbeatsLayout.addWidget(foldAllAdvanvced)

        unfoldAllAdvanvced = QtWidgets.QPushButton(translate("trackEditorPythonFile", "unfold all advanced"))
        unfoldAllAdvanvced.clicked.connect(self.unfoldAllAdvanced)
        allUpbeatsLayout.addWidget(unfoldAllAdvanvced)

    def updateTrackWidgets(self, listOfStaticTrackRepresentations):
        """React to the backend adding, deleting, hiding or moving tracks.
        A track widget persists until the track gets deleted. No re-creation on every change. """
        leftOver = list(self.tracks.keys())
        visibleTracks = []
        trackWidgetsInOrder = []
        nameLineEditsInOrder = []
        upbeatSpinBoxsInOrder = []
        for trackExportObject in listOfStaticTrackRepresentations:
            #First are all normal, visible tracks.
            #After that the tracks have a property "hiddenPosition" for hidden tracks that need individual sorting.
            if not trackExportObject["id"] in self.tracks: #A brand new track
                widget = TrackWidget(self, trackExportObject)
                self.tracks[trackExportObject["id"]] = widget
                self.layout.insertWidget(-1, widget)
                widget.setTitle("id: {}".format(trackExportObject["id"]))
            else:
                leftOver.remove(trackExportObject["id"])

            self.tracks[trackExportObject["id"]].ui.deleteButton.setEnabled(True)
            self.tracks[trackExportObject["id"]].blockSignals(True)
            self.tracks[trackExportObject["id"]].updateData(trackExportObject) #This has a sideeffect of updating widgets, which will change backend data and trigger a recursive updateTrackWidgets. We need to block the signals. self.tracks has the track widgets.
            self.tracks[trackExportObject["id"]].blockSignals(False)
            nameLineEditsInOrder.append(self.tracks[trackExportObject["id"]].ui.nameLineEdit)

            trackWidgetsInOrder.append(self.tracks[trackExportObject["id"]])
            if trackExportObject["hiddenPosition"] is None:
                visibleTracks.append(trackExportObject)

        for trId in leftOver: #track still exist here but not in the backend. Delete.
            w = self.tracks[trId]
            del self.tracks[trId]
            w.setParent(None)
            w.close()

        #Sort tracks as they are in the backend/score-view
        for widget in trackWidgetsInOrder: #visibleTracks is sorted and does not have empty tracks.
            self.layout.removeWidget(widget)
        for widget in trackWidgetsInOrder:
            #Re-Insert all, this time in order.
            self.layout.insertWidget(-1, widget)

        #Create tab order. Those work in pairs, linked list. a before b. And multiple of those pairs need to be in order itself.
        if len(nameLineEditsInOrder) > 1:
            for pair in pairwise(nameLineEditsInOrder):
                self.setTabOrder(*pair) #takes exactly two arguments

        if len(visibleTracks) == 1:
            w = self.tracks[visibleTracks[0]["id"]]
            w.ui.deleteButton.setEnabled(False) #it is not possible to delete the only visible track. The backend will prevent it but we don't even offer the choice here.

    def setAllUpbeats(self):
        for trackWidget in self.tracks.values():
            trackWidget.ui.upbeatSpinBox.setValue(self.allUpbeatsCombinedTickWidget.value())

    def foldAllAdvanvced(self):
        for trackWidget in self.tracks.values():
            trackWidget.ui.advanced.setChecked(False)

    def unfoldAllAdvanced(self):
        for trackWidget in self.tracks.values():
            trackWidget.ui.advanced.setChecked(True)


    def keyPressEvent(self, event):
        """Escape closes the track editor"""
        k = event.key()  #49=1, 50=2 etc.
        if k == QtCore.Qt.Key_Escape:
            self.mainWindow.ui.actionData_Editor.setChecked(False)
            self.mainWindow.toggleMainView()
        super().keyPressEvent(event)

"""
api.callbacks.updateBlockTrack.append(lambda trId, blocksExportData: self.tracks[trId].blockScene.regenerateBlocks(blocksExportData))

class TrackBlockScene(QtWidgets.QGraphicsScene):
    def __init__(self, parentWidget):
        super().__init__()
        self.blocks = []
        self.parentWidget = parentWidget


    def mousePressEvent(self, event):
        button = event.button()
        if button == 1: #left
            self.setEnabled(False)
        super().mousePressEvent(event)

    def mouseReleaseEvent(self, event):
        button = event.button()
        if button == 1: #left
            self.setEnabled(True)
        super().mouseReleaseEvent(event)

    def regenerateBlocks(self, blocksExportData):
        self.clear()
        self.blocks = []

        dur = 0
        for backendBlock in blocksExportData:
            b = BlockGraphicsItem(self, backendBlock)
            self.blocks.append(b)
            self.addItem(b)
            b.setPos(backendBlock["tickindex"] / constantsAndConfigs.blocksTicksToPixelRatio, 0)
            dur += backendBlock["completeDuration"]

        self.parentWidget.ui.blocks.setFixedSize(dur / constantsAndConfigs.blocksTicksToPixelRatio, 30)


class BlockGraphicsItem(QtWidgets.QGraphicsRectItem):
    def __init__(self, parentTrackWidget, blockExportObject):
        width = blockExportObject["completeDuration"] / constantsAndConfigs.blocksTicksToPixelRatio
        super().__init__(0,0,width, 30) #x, y, w, h

        self.setBrush(self.stringToColor(blockExportObject["name"]))
        #self.setFlags(QtWidgets.QGraphicsItem.ItemSendsGeometryChanges|QtWidgets.QGraphicsItem.ItemIsFocusable)
        #self.setCursor(QtCore.Qt.SizeAllCursor)

    def stringToColor(self, st):
        if st:
            c = md5(st.encode()).hexdigest()
            return QtGui.QColor(int(c[0:9],16) % 255, int(c[10:19],16) % 255, int(c[20:29],16)% 255, 255)
        else:
            return QtGui.QColor(255,255,255,255) #Return White
"""

