from global_widgets.global_spinbox import labelledSpin
from widget_library.ok_cancel_buttons_widget import (
    OkButtonWidget,
    CancelButtonWidget,
    OkSendButtonWidget,
)

# from global_widgets.global_send_popup import SetConfirmPopup
from widget_library.spin_buttons_widget import SpinButton

from PySide2 import QtWidgets, QtGui, QtCore
from handler_library.handler import PayloadHandler
import logging
import json


class ModeHandler(PayloadHandler):

    modeSwitched = QtCore.Signal(str)
    UpdateModes = QtCore.Signal(dict)
    OpenPopup = QtCore.Signal(PayloadHandler, list)
    settingToggle = QtCore.Signal(str)

    def __init__(self, NativeUI, *args, **kwargs):
        super().__init__(["TARGET"], *args, name="Mode handler", **kwargs)
        # super(TabModes, self).__init__(NativeUI, *args, **kwargs)
        self.NativeUI = NativeUI
        self.spinDict = {}
        self.buttonDict = {}
        self.radioDict = {}
        self.commandList = []
        self.mainSpinDict = {}
        self.mainButtonDict = {}
        self.modeList = (
            self.NativeUI.modeList
        )  # ["PC/AC", "PC/AC-PRVC", "PC-PSV", "CPAP", 'CURRENT']
        self.manuallyUpdatedBoolDict = {mode: False for mode in self.modeList}
        self.mainManuallyUpdated = False
        #self.activeMode = self.modeList[0]

        with open("NativeUI/configs/mode_config.json") as json_file:
            modeDict = json.load(json_file)

        self.relevantKeys = [setting[2] for setting in modeDict["settings"]]

    def add_widget(self, widget: QtWidgets.QWidget, key: str):
        if isinstance(widget, labelledSpin):
            self.spinDict[key] = widget
        if (
            isinstance(widget, OkButtonWidget)
            or isinstance(widget, CancelButtonWidget)
            or isinstance(widget, OkSendButtonWidget)
        ):
            if self.get_mode(key) == "CURRENT":
                self.mainButtonDict[key] = widget
            else:
                self.buttonDict[key] = widget
        if isinstance(widget, QtWidgets.QRadioButton):
            self.radioDict[key] = widget
        if isinstance(widget, SpinButton):
            self.mainSpinDict[key] = widget
        if isinstance(widget, QtWidgets.QStackedWidget):
            self.stack = widget

    def on_timeout(self, *args) -> int:
        target_data = self.get_db()
        logging.debug(target_data)
        outdict = {}

        for key in self.relevantKeys:
            try:
                outdict[key] = target_data[key]
            except KeyError:
                logging.debug("Invalid key %s in measurement database", key)
        self.UpdateModes.emit(outdict)
        return 0

    def handle_okbutton_click(self, key: str):
        mode = self.get_mode(key)
        message, command = [], []
        for widget in self.spinDict:
            if (mode in widget) and self.spinDict[widget].manuallyUpdated:
                setVal = self.spinDict[widget].get_value()
                setVal = round(setVal, self.spinDict[widget].decPlaces)
                message.append("set" + widget + " to " + str(setVal))
                command.append(
                    [
                        self.spinDict[widget].cmd_type,
                        self.spinDict[widget].cmd_code,
                        setVal,
                    ]
                )
        if isinstance(self.buttonDict[key], OkSendButtonWidget):
            message.append("change mode to " + str(mode))
            command.append(["SET_MODE", mode.replace("/", "_").replace("-", "_")])
        # create a signal emitting message, command, handler identifier - in nativeui connect to a popup widget
        # command sending should occur in handler
        self.commandList = command
        self.OpenPopup.emit(self, message)

    def handle_mainokbutton_click(self):
        message, command = [], []
        for widget in self.mainSpinDict:
            if self.mainSpinDict[widget].manuallyUpdated:
                setVal = self.mainSpinDict[widget].get_value()
                setVal = round(setVal, self.mainSpinDict[widget].decPlaces)
                message.append("set" + widget + " to " + str(setVal))
                command.append(
                    [
                        self.mainSpinDict[widget].cmd_type,
                        self.mainSpinDict[widget].cmd_code,
                        setVal,
                    ]
                )
        # create a signal emitting message, command, handler identifier - in nativeui connect to a popup widget
        # command sending should occur in handler
        self.commandList = command

        self.OpenPopup.emit(self, message)

    def sendCommands(self):
        if self.commandList == []:
            a = 1
        else:
            for command in self.commandList:
                logging.info("sending commands:", command)
                self.NativeUI.q_send_cmd(*command)
                if 'SET_MODE' in command:
                    self.modeSwitched.emit(command[-1])
            if 'SET_TARGET' in command[0]:
                mode = self.get_mode(command[0])
                self.commandSent(mode)
        return 0

    def handle_cancel_pressed(self, buttonMode: str):
        logging.debug('handle cancel button press in mode')
        for widget in self.spinDict:
            if buttonMode in widget or self.NativeUI.currentMode in widget:
                self.spinDict[widget].manuallyUpdated = False
                logging.debug('checking')
                logging.debug(buttonMode)
                logging.debug(self.NativeUI.currentMode)
                logging.debug(widget)
        logging.debug('cancel pressed')
        logging.debug(buttonMode)
        logging.debug(self.NativeUI.currentMode)
        if buttonMode == self.NativeUI.currentMode or buttonMode == 'CURRENT':
            logging.debug("modes match ")
            for widget in self.mainSpinDict:
                self.mainSpinDict[widget].manuallyUpdated = False
        else:
            logging.debug("do nothing in clinical")
        #self.active_payload()
        self.on_timeout()
        self.refresh_button_colour()
        self.refresh_main_button_colour()

    def commandSent(self, key = 'CURRENT'):
        self.commandList = []
        logging.debug('going to use ')
        logging.debug(key)
        mode = self.get_mode(key)
        if mode == 'CURRENT':
            mode == self.NativeUI.currentMode
        for widget in self.spinDict:
            if mode.replace("/", "_").replace("-", "_") in widget.replace("/", "_").replace("-", "_"):#self.NativeUI.currentMode.replace("/", "_").replace("-", "_") in widget:
                logging.debug('setting false ' + widget)
                self.spinDict[widget].manuallyUpdated = False
        for widget in self.mainSpinDict:
            self.mainSpinDict[widget].manuallyUpdated = False

        self.NativeUI.q_send_cmd('GET_TARGETS', mode.replace("/", "_").replace("-", "_"))
        #self.active_payload()
        self.on_timeout()
        self.refresh_button_colour()
        self.refresh_main_button_colour()

    def handle_manual_change(self, changed_spin_key):
        #self.active_payload()
        self.on_timeout()
        self.refresh_button_colour()
        self.refresh_main_button_colour()

    def handle_radio_toggled(self, radioButtonState: bool, radioKey: str):
        """On radio toggle enable/disable corresponding spin box. Also modify the values shown on the main page"""
        mode = self.get_mode(radioKey)
        spinKey = radioKey.replace("radio", "spin")
        spinBox = self.spinDict[spinKey]
        spinBox.setEnabled(radioButtonState)
        if mode == self.NativeUI.currentMode and radioButtonState:
            mainKey = spinKey.replace(mode + "_", "").replace("spin", "CURRENT")
            self.stack.setCurrentWidget(self.mainSpinDict[mainKey])
        return 0

    def refresh_button_colour(self):
        self.manuallyUpdatedBoolDict = {mode: False for mode in self.modeList}
        for spin in self.spinDict:
            mode = self.get_mode(spin)
            if mode == None:
                continue
            self.manuallyUpdatedBoolDict[mode] = (
                self.manuallyUpdatedBoolDict[mode]
                or self.spinDict[spin].manuallyUpdated
            )
        for button in self.buttonDict:
            mode = str(self.get_mode(button))
            if isinstance(self.buttonDict[button], OkSendButtonWidget) and (
                mode != self.NativeUI.currentMode
            ):
                self.buttonDict[button].setColour(str(int(True)))
            else:
                try:
                    self.buttonDict[button].setColour(
                        str(int(self.manuallyUpdatedBoolDict[mode]))
                    )
                except KeyError as e:
                    logging.warning(str(e))
        return 0

    def propagate_modevalchange(self, widget: QtWidgets.QWidget):
        """Called when a value is changed in clinical limits page. Ensures all spin boxes representing the same values
        actually display the same values when user edits. """
        if self.NativeUI.currentMode.replace("/", "_").replace("-", "_") not in widget.cmd_type:
            return 0 # do not propagate
        for spin in self.mainSpinDict.values():
            if spin.tag == widget.tag:
                if spin.get_value() != widget.get_value():
                    spin.set_value(widget.get_value())

        for spin in self.spinDict.values():
            if spin.tag == widget.tag:
                if (
                    self.NativeUI.currentMode.replace("/", "_").replace("-", "_")
                    in spin.cmd_type
                ):
                    if spin.get_value() != widget.get_value() and widget != spin:
                        spin.signalling_spin.set_value(widget.get_value())

        self.refresh_button_colour()

    def refresh_main_button_colour(self):
        self.manuallyUpdatedBoolDict["CURRENT"] = False
        for spin in self.mainSpinDict:
            self.manuallyUpdatedBoolDict["CURRENT"] = (
                self.manuallyUpdatedBoolDict["CURRENT"]
                or self.mainSpinDict[spin].manuallyUpdated
            )
        for button in self.mainButtonDict:
            self.mainButtonDict[button].setColour(
                str(int(self.manuallyUpdatedBoolDict["CURRENT"]))
            )

    def get_mode(self, key: str):
        """Get mode from the widget's corresponding key."""
        for mode in self.modeList + ["CURRENT"]:
            if mode.replace("/", "_").replace("-", "_") in key.replace("/", "_").replace("-", "_"):
                return mode
        logging.debug('failed to get mode out of')
        logging.debug(key)
