#!/usr/bin/env python3
# qtvcp
#
# Copyright (c) 2018  Chris Morley <chrisinnanaimo@hotmail.com>
#
# This program 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 2 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.
#
#################################################################################

import os

from qtpy.QtCore import Signal, Property, QVariant
from qtpy.QtWidgets import QLabel
from qtpy.QtGui import QPixmap

from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status, Info, Tool, Path
from qtvcp import logger

# Instantiate the libraries with global reference
# STATUS gives us status messages from linuxcnc
# LOG is for running code logging
STATUS = Status()
INFO = Info()
TOOL = Tool()
PATH = Path()
LOG = logger.getLogger(__name__)

# Force the log level for this module
# LOG.setLevel(logger.INFO) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL

if PATH.IMAGEDIR is not None:
    DEFAULTIMAGE = (os.path.join(PATH.IMAGEDIR,'applet-critical.png')) or ''
else:
    DEFAULTIMAGE = ''

class ImageSwitcher(QLabel, _HalWidgetBase):
    widgetChanged = Signal(int)

    def __init__(self, parent=None):
        super(ImageSwitcher, self).__init__(parent)
        self._defaultImage = DEFAULTIMAGE
        self._imagePath = [DEFAULTIMAGE]
        self._current_number = 0
        #self.setScaledContents(True)
        self.defaultPixmap = QPixmap()

    def _hal_init(self):
        self.show_image_by_number(self._current_number)

    def _designerInit(self):
        self._hal_init()

    # Show the widgets based on a reference number
    def show_image_by_number(self, number):
        #print (self.objectName(),len(self._imagePath),number)
        try:
            # outside limits or list
            if number <0 or number > len(self._imagePath)-1:
                LOG.debug('Path reference number out of range: {}'.format(number))
                self.setPixmap(self.defaultPixmap)
                return
            # specifically ask for None
            if self._imagePath[number].upper() == 'NONE':
                self.setPixmap(QPixmap())
                return
            # specifically ask for Default image
            if self._imagePath[number].upper() == 'DEFAULT':
                self.setPixmap(self.defaultPixmap)
                return
            # resources file images.
            if ':/' in self._imagePath[number]:
                path = self._imagePath[number]
                pixmap = QPixmap(path)
                # no valid image
                if pixmap.isNull():
                    self.setPixmap(self.defaultPixmap)
                    return
                self.setPixmap(pixmap)
                return
            else:
                path = os.path.expanduser(self._imagePath[number])
        except Exception as e:
            LOG.error('Path reference number: {}'.format(e))
            self.setPixmap(self.defaultPixmap)
            return
            path = os.path.expanduser(self.defaultPixmap)
        #print 'requested:',number,self._imagePath[number]
        # if path doesn't exist try referencing
        # from the built in image folder
        if not os.path.exists(path):
            path = os.path.join(PATH.IMAGEDIR, path)
            if not os.path.exists(path):
                LOG.debug('No Path: {}'.format(path))
        pixmap = QPixmap(path)
        self.setPixmap(pixmap)

    def set_default_image(self, path):
        self._defaultImage = path

    def set_image_number(self, data):
        self._current_number = data
        self.show_image_by_number(data)
    def get_image_number(self):
        return self._current_number
    def reset_image_number(self):
        self._current_number = 0
    image_number = Property(int, get_image_number, set_image_number, reset_image_number)

    def set_image_l(self, data):
        self._imagePath = data
    def get_image_l(self):
        return self._imagePath
    def reset_image_l(self):
        self._imagePath = [self._defaultImage]
    image_list = Property(QVariant.typeToName(QVariant.StringList), get_image_l, set_image_l, reset_image_l)

    def setImagePath(self, data):
        self.defaultPixmap = data
    def getImagePath(self):
        return self.defaultPixmap
    def resetImagePath(self):
        self.defaultPixmap

    image_default = Property(QPixmap, getImagePath, setImagePath, resetImagePath)


    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)



class StatusImageSwitcher(ImageSwitcher):

    def __init__(self, parent=None):
        super(StatusImageSwitcher, self).__init__(parent)
        self._imagePath = [os.path.join(PATH.IMAGEDIR,'applet-critical.png'),
                    os.path.join(PATH.IMAGEDIR,'spindle_ccw.gif'),
                    os.path.join(PATH.IMAGEDIR,'spindle_cw.gif')]
        self.spindle = True
        self.all_homed = False
        self.axis_homed = False
        self.hard_limits = False
        self.machine_state = False
        self.command_state = False
        self.feedmode_state = False
        self.spindlemode_state = False
        self.toolnumber_state = False
        self.tool_orientation_state = False

        self._last_limit = []
        self.axis = 'X'
        for i in range(0,len(INFO.AVAILABLE_JOINTS)):
            self._last_limit.append([0,0])

    def _hal_init(self):
        if self.spindle:
            STATUS.connect('spindle-control-changed', lambda w, a, b, c, d: self.switch_on_spindle(b,c,d))
        elif self.all_homed:
            STATUS.connect('not-all-homed', lambda w, data: self.switch_on_homed(0))
            STATUS.connect('all-homed', lambda w: self.switch_on_homed(1))
        elif self.axis_homed:
            STATUS.connect('unhomed', lambda w, data: self.switch_on_axis_unhomed(data))
            STATUS.connect('homed', lambda w, data: self.switch_on_axis_homed(data))
        elif self.hard_limits:
            STATUS.connect('hard-limits-tripped', lambda w, data, group: self.switch_on_hard_limits(data, group))
        elif self.machine_state:
            STATUS.connect('state-estop', lambda w: self.switch_on_machine_state(0))
            STATUS.connect('interp-run', lambda w: self.switch_on_machine_state(1))
            STATUS.connect('interp-idle', lambda w: self.switch_on_machine_state(2))
            STATUS.connect('interp-paused', lambda w: self.switch_on_machine_state(3))
            STATUS.connect('interp-waiting', lambda w: self.switch_on_machine_state(4))
            STATUS.connect('interp-reading', lambda w: self.switch_on_machine_state(5))
        elif self.command_state:
            STATUS.connect('command-running', lambda w: self.switch_on_command_state(0))
            STATUS.connect('command-stopped', lambda w: self.switch_on_command_state(1))
            STATUS.connect('command-error', lambda w: self.switch_on_command_state(2))
        elif self.feedmode_state:
            STATUS.connect('fpm-mode', lambda w, d: self.switch_on_feedmode_state(0, d))
            STATUS.connect('fpr-mode', lambda w, d: self.switch_on_feedmode_state(1,d))
            STATUS.connect('itime-mode', lambda w, d: self.switch_on_feedmode_state(2,d))
        elif self.spindlemode_state:
            STATUS.connect('rpm-mode', lambda w, d: self.switch_on_spindlemode_state(0, d))
            STATUS.connect('css-mode', lambda w, d: self.switch_on_spindlemode_state(1, d))
        elif self.toolnumber_state:
            STATUS.connect('tool-in-spindle-changed', lambda w, d:self.switch_on_toolnumber_state(d))
        elif self.tool_orientation_state:
            STATUS.connect('tool-info-changed', lambda w, d:self.switch_on_toolinfo_state(d, TOOL.ORIENTATION))


    def _designerInit(self):
        self.show_image_by_number(0)

    # M5 disabled = image 0
    # M3 at speed = image 1
    # M4 at speed = image 2
    # M3 accelerating = image 3
    # M4 accelerating = image 4
    def switch_on_spindle(self, on, speed, up):
        if not on:
            num = 0
        elif not up:
            if speed<0: num = 4
            else: num = 3
        elif speed <0: num = 2
        else: num = 1
        self.set_image_number(num)

    def switch_on_homed(self, data):
        if not data <0:
            self.set_image_number(data)

    def switch_on_axis_homed(self, num):
        data = INFO.GET_NAME_FROM_JOINT[int(num)]
        if data == self.axis:
            self.set_image_number(1)

    def switch_on_axis_unhomed(self, num):
        data = INFO.GET_NAME_FROM_JOINT[int(num)]
        if data == self.axis:
            self.set_image_number(0)

    def switch_on_hard_limits(self, data, group):
        if not data:
            # tripped
            self.set_image_number(0)
        elif (len(self._imagePath)) == 2:
            #print 'bool images'
            self.set_image_number(1)
        elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS)):
            #print 'per joint limits images', self._last_limit, group
            for i in range(0,len(INFO.AVAILABLE_JOINTS)):
                if group[i] == self._last_limit[i]:
                    pass
                elif group[i] == [0,0]:
                    pass
                else:
                    self.set_image_number(i+1)
                    break
        elif (len(self._imagePath)-1) == (len(INFO.AVAILABLE_JOINTS) * 2):
            pass
            #print 'per joint and per end limits images'
        self._last_limit = group

    def switch_on_machine_state(self, state):
        self.set_image_number(state)

    def switch_on_command_state(self, state):
        self.set_image_number(state)

    def switch_on_feedmode_state(self, mode, state):
        if state:
            self.set_image_number(mode)

    def switch_on_spindlemode_state(self, mode, state):
        if state:
            self.set_image_number(mode)

    def switch_on_toolnumber_state(self, number):
        self.set_image_number(number)

    def switch_on_toolinfo_state(self, tool_entry, index):
        toolnum = tool_entry[0]
        tool_table_line = TOOL.GET_TOOL_INFO(toolnum)
        try:
            self.set_image_number(tool_table_line[index])
        except Exception as e:
            LOG.warning('Problem with tool file info')

    #########################################################################
    # This is how designer can interact with our widget properties.
    # designer will show the Property properties in the editor
    # it will use the get set and reset calls to do those actions
    #
    # _toggle_properties makes it so we can only select one option
    ########################################################################

    def _toggle_properties(self, picked):
        data = ('spindle','all_homed', 'axis_homed','hard_limits',
                'machine_state', 'command_state', 'feedmode_state',
                'spindlemode_state', 'tool_orientation_state',
                'toolnumber_state' )

        for i in data:
            if not i == picked:
                self['watch_'+i] = False

# property getter/setters

    # machine_spindle status
    def set_spindle(self, data):
        self.spindle = data
        if data:
            self._toggle_properties('spindle')
    def get_spindle(self):
        return self.spindle
    def reset_spindle(self):
        self.spindle = False
    watch_spindle = Property(bool, get_spindle, set_spindle, reset_spindle)

    # machine_homed status
    def set_homed(self, data):
        self.all_homed = data
        if data:
            self._toggle_properties('all_homed')
    def get_homed(self):
        return self.all_homed
    def reset_homed(self):
        self.all_homed = False
    watch_all_homed = Property(bool, get_homed, set_homed, reset_homed)

    # machine_axis_homed status
    def set_axis_homed(self, data):
        self.axis_homed = data
        if data:
            self._toggle_properties('axis_homed')
    def get_axis_homed(self):
        return self.axis_homed
    def reset_axis_homed(self):
        self.axis_homed = False
    watch_axis_homed = Property(bool, get_axis_homed, set_axis_homed, reset_axis_homed)


    # machine_limits status
    def set_limits(self, data):
        self.hard_limits = data
        if data:
            self._toggle_properties('hard_limits')
    def get_limits(self):
        return self.hard_limits
    def reset_limits(self):
        self.hard_limits = False
    watch_hard_limits = Property(bool, get_limits, set_limits, reset_limits)

    # machine_state status
    def set_machine_state(self, data):
        self.machine_state = data
        if data:
            self._toggle_properties('machine_state')
    def get_machine_state(self):
        return self.machine_state
    def reset_machine_state(self):
        self.machine_state = False
    watch_machine_state = Property(bool, get_machine_state, set_machine_state,
                                                      reset_machine_state)

    # command_state status
    def set_command_state(self, data):
        self.command_state = data
        if data:
            self._toggle_properties('command_state')
    def get_command_state(self):
        return self.command_state
    def reset_command_state(self):
        self.command_state = False
    watch_command_state = Property(bool, get_command_state, set_command_state,
                                                      reset_command_state)

    # feedmode_state status
    def set_feedmode_state(self, data):
        self.feedmode_state = data
        if data:
            self._toggle_properties('feedmode_state')
    def get_feedmode_state(self):
        return self.feedmode_state
    def reset_feedmode_state(self):
        self.feedmode_state = False
    watch_feedmode_state = Property(bool, get_feedmode_state, set_feedmode_state,
                                                      reset_feedmode_state)
    # spindlemode_state status
    def set_spindlemode_state(self, data):
        self.spindlemode_state = data
        if data:
            self._toggle_properties('spindlemode_state')
    def get_spindlemode_state(self):
        return self.spindlemode_state
    def reset_spindlemode_state(self):
        self.spindlemode_state = False
    watch_spindlemode_state = Property(bool, get_spindlemode_state, set_spindlemode_state,
                                                      reset_spindlemode_state)

    # toolnumber_state status
    def set_toolnumber_state(self, data):
        self.toolnumber_state = data
        if data:
            self._toggle_properties('toolnumber_state')
    def get_toolnumber_state(self):
        return self.toolnumber_state
    def reset_toolnumber_state(self):
        self.toolnumber_state = False
    watch_toolnumber_state = Property(bool, get_toolnumber_state, set_toolnumber_state,
                                                      reset_toolnumber_state)

    # tool_orientation_state status
    def set_tool_orientation_state(self, data):
        self.tool_orientation_state = data
        if data:
            self._toggle_properties('tool_orientation_state')
    def get_tool_orientation_state(self):
        return self.tool_orientation_state
    def reset_tool_orientation_state(self):
        self.tool_orientation_state = False
    watch_tool_orientation_state = Property(bool, get_tool_orientation_state, set_tool_orientation_state,
                                                      reset_tool_orientation_state)

    def set_axis(self, data):
        if data.upper() in('X','Y','Z','A','B','C','U','V','W'):
            self.axis = data.upper()
    def get_axis(self):
        return self.axis
    def reset_axis(self):
        self.axis = 'X'
    axis_letter = Property(str, get_axis, set_axis, reset_axis)

    ##############################
    # required class boiler code #
    ##############################

    def __getitem__(self, item):
        return getattr(self, item)
    def __setitem__(self, item, value):
        return setattr(self, item, value)



# for testing without editor:
def main():
    import sys
    from qtpy.QtWidgets import QApplication
    app = QApplication(sys.argv)
    widget = ImageSwitcher()
    widget._hal_init()

    widget.show()
    sys.exit(app.exec_())
if __name__ == "__main__":
    main()

