#!/usr/bin/env python3
#
# QTVcp Widget
# Copyright (c) 2018 Chris Morley
#
# 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.
###############################################################################

from qtpy.QtWidgets import QWidget, QProgressBar, QToolButton, QHBoxLayout, QMenu, QAction
from qtpy.QtCore import Qt, Property, QBasicTimer, Signal
from qtpy.QtGui import QPainter, QFont, QIcon

from qtvcp.widgets.widget_baseclass import _HalWidgetBase
from qtvcp.core import Status, Action, Info
from qtvcp.lib.aux_program_loader import Aux_program_loader
from qtvcp import logger

# Instiniate the libraries with global reference
# STATUS gives us status messages from LinuxCNC
# AUX_PRGM holds helper program loader
# INI holds INI details
# ACTION gives commands to LinuxCNC
# LOG is for running code logging
STATUS = Status()
AUX_PRGM = Aux_program_loader()
INFO = Info()
ACTION = Action()
LOG = logger.getLogger(__name__)
# Force the log level for this module
#LOG.setLevel(logger.DEBUG) # One of DEBUG, INFO, WARNING, ERROR, CRITICAL


# So we can customize the label
# meaning we can use a new line
# to have two levels of text
class LabeledBar(QProgressBar):
    def __init__(self, parent=None):
        super(LabeledBar, self).__init__(parent)
        self.text = ''
        self.setTextVisible(False)
        self.font = QFont()
        self.font.setPointSize(8)
        self.font.setBold(True)
        self.font.setWeight(75)

    def paintEvent(self, event):
        super(LabeledBar, self).paintEvent(event)
        qp = QPainter()
        qp.begin(self)
        self.drawText(qp)
        qp.end()

    def drawText(self, qp):
        qp.setOpacity(1.0)
        qp.setFont(self.font)
        qp.drawText(self.rect(), Qt.AlignCenter, self.text)


class HAdjustmentBar(QWidget):
    valueChanged = Signal(int)
    def __init__(self, parent=None):
        super(HAdjustmentBar, self).__init__(parent)
        self.value = 50
        self.step = 1
        self.hi_value = 100
        self.low_value = 50
        self.timer_value = 100
        self.texttemplate = 'Value =  %s'
        self.timer = QBasicTimer()
        self.showToggleButton = True
        self.showSettingMenu = True
        self.bar = LabeledBar()
        #self.bar.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
        # black magic class patching
        # so calling these functions actually calls the self.bar's functions.
        self.minimum = self.bar.minimum
        self.maximum = self.bar.maximum

    def buildWidget(self):
        layout = QHBoxLayout(self)
        layout.setContentsMargins(0,0,0,0)
        SettingMenu = QMenu()
        exitButton = QAction(QIcon('exit24.png'), 'Set As High', self)
        exitButton.triggered.connect(self.setHigh)
        SettingMenu.addAction(exitButton)
        setlowButton = QAction(QIcon('exit24.png'), 'Set As Low', self)
        setlowButton.triggered.connect(self.setLow)
        SettingMenu.addAction(setlowButton)

        self.tb_down = QToolButton()
        self.tb_down.pressed.connect(self.on_click_down)
        self.tb_down.released.connect(self.on_released)
        self.tb_down.setArrowType(Qt.LeftArrow)

        self.tb_up = QToolButton()
        self.tb_up.pressed.connect(self.on_click_up)
        self.tb_up.released.connect(self.on_released)
        self.tb_up.setArrowType(Qt.RightArrow)

        if self.showToggleButton:
            tb_set = QToolButton()
            tb_set.clicked.connect(self.on_click_set_value)
            tb_set.setText('<>')
            if self.showSettingMenu:
                tb_set.setMenu(SettingMenu)
                tb_set.setPopupMode(QToolButton.DelayedPopup)

        layout.addWidget(self.tb_down)
        layout.addWidget(self.bar)
        layout.addWidget(self.tb_up)
        if self.showToggleButton:
            layout.addWidget(tb_set)
        layout.setSpacing(0)

    def on_click_up(self):
        self.timer.start(self.timer_value, self)
        self.value += self.step
        self._setValue()

    def on_click_down(self):
        self.timer.start(self.timer_value, self)
        self.value -= self.step
        self._setValue()
    def on_released(self):
        self.timer.stop()

    def on_click_set_value(self):
        if self.value == self.hi_value:
            self.value = self.low_value
        else:
            self.value = self.hi_value
        self._setValue()

    def timerEvent(self, e):
        if self.value < self.maximum() and self.value > self.minimum():
            if self.tb_down.isDown():
                self.value -= self.step
            else:
                self.value += self.step
            self._setValue()
        else:
            self.timer.stop()

    def _setValue(self):
        if self.value < self.minimum():self. value = self.minimum()
        if self.value > self.maximum(): self.value = self.maximum()
        self.valueChanged.emit(self.value)

    def setValue(self, value):
        if value < self.minimum(): value = self.minimum()
        if value > self.maximum(): value = self.maximum()
        self.value = int(value)
        tmpl = lambda s: self.texttemplate % s
        try:
            self.bar.text = tmpl(int(value))
        except:
            self.bar.text = self.texttemplate
        self.bar.setValue(int(value))
        self.bar.update()

    def setHigh(self):
        self.hi_value = self.value

    def setLow(self):
        self.low_value = self.value

    def setMaximum(self, data):
        if data < self.hi_value:
            self.hi_value = data
        self.bar.setMaximum(data)

    def setMinimum(self, data):
        if data > self.low_value:
            self.low_value = data
        self.bar.setMinimum(data)

    def setStep(self, data):
        self.step = data

class StatusAdjustmentBar(HAdjustmentBar, _HalWidgetBase):
    def __init__(self, parent=None):
        super(StatusAdjustmentBar, self).__init__(parent)
        self._block_signal = False
        self.rapid = True
        self.feed = False
        self.spindle = False
        self.jograte = False
        self.jograte_angular = False
        self.maxv = False
        self.texttemplate = 'Value =  %s'

    # self.PREFS_
    # self.HAL_NAME_
    # comes from base class
    def _hal_init(self):

        # if we build the widget before now, designer options are not
        # taken account
        self.buildWidget()

        # when estopped disable buttons
        STATUS.connect('state-estop', lambda w: self.setEnabled(False))
        STATUS.connect('state-estop-reset', lambda w: self.setEnabled(True))

        # set options
        if self.rapid:
            STATUS.connect('rapid-override-changed', lambda w, data: self.setValue(data))
            self.setMaximum(100)
        elif self.feed:
            STATUS.connect('feed-override-changed', lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_FEED_OVERRIDE))
        elif self.spindle:
            STATUS.connect('spindle-override-changed', lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_SPINDLE_OVERRIDE))
            self.setMinimum(int(INFO.MIN_SPINDLE_OVERRIDE))
        elif self.jograte:
            STATUS.connect('jograte-changed', lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_LINEAR_JOG_VEL))
        elif self.jograte_angular:
            STATUS.connect('jograte-angular-changed', lambda w, data: self.setValue(data))
            print(int(INFO.MAX_ANGULAR_JOG_VEL))
            self.setMaximum(int(INFO.MAX_ANGULAR_JOG_VEL))
        elif self.maxv:
            STATUS.connect('max-velocity-override-changed', lambda w, data: self.setValue(data))
            self.setMaximum(int(INFO.MAX_TRAJ_VELOCITY))
        else:
           LOG.error('{} : no option recognised'.format(self.HAL_NAME_))

        # If there is a preference file object use it to load the hi/low toggle points
        if self.PREFS_:
            self.hi_value = self.PREFS_.getpref(self.HAL_NAME_+'-hi-value', 75, int, 'SCREEN_CONTROL_LAST_SETTING')
            self.low_value = self.PREFS_.getpref(self.HAL_NAME_+'-low-value', 25, int, 'SCREEN_CONTROL_LAST_SETTING')

        # connect a signal and callback function to the button
        self.valueChanged.connect(self._action)

    # when qtvcp closes this gets called
    def _hal_cleanup(self):
        if self.PREFS_:
            LOG.debug('Saving {} data to file.'.format(self.HAL_NAME_))
            self.PREFS_.putpref(self.HAL_NAME_+'-hi-value', self.hi_value, int, 'SCREEN_CONTROL_LAST_SETTING')
            self.PREFS_.putpref(self.HAL_NAME_+'-low-value', self.low_value, int, 'SCREEN_CONTROL_LAST_SETTING')

    def _action(self, value):
        if self.rapid:
            ACTION.SET_RAPID_RATE(value)
        elif self.feed:
            ACTION.SET_FEED_RATE(value)
        elif self.spindle:
            ACTION.SET_SPINDLE_RATE(value)
        elif self.jograte:
            ACTION.SET_JOG_RATE(value)
        elif self.jograte_angular:
            ACTION.SET_JOG_RATE_ANGULAR(value)
        elif self.maxv:
            ACTION.SET_MAX_VELOCITY_RATE(value)
        else:
           LOG.error('{} no action recognised'.format(self.HAL_NAME_))
    #########################################################################
    # 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 = ('rapid', 'feed', 'spindle', 'jograte', 'jograte_angular', 'maxv')

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

    def setrapid(self, data):
        self.rapid = data
        if data:
            self._toggle_properties('rapid')
    def getrapid(self):
        return self.rapid
    def resetrapid(self):
        self.rapid = False

    def setfeed(self, data):
        self.feed = data
        if data:
            self._toggle_properties('feed')
    def getfeed(self):
        return self.feed
    def resetfeed(self):
        self.feed = False

    def setspindle(self, data):
        self.spindle = data
        if data:
            self._toggle_properties('spindle')
    def getspindle(self):
        return self.spindle
    def resetspindle(self):
        self.spindle = False

    def setjograte(self, data):
        self.jograte = data
        if data:
            self._toggle_properties('jograte')
    def getjograte(self):
        return self.jograte
    def resetjograte(self):
        self.jograte = False

    def setjograteangular(self, data):
        self.jograte_angular = data
        if data:
            self._toggle_properties('jograte_angular')
    def getjograteangular(self):
        return self.jograte_angular
    def resetjograteangular(self):
        self.jograte_angular = False

    def setmaxv(self, data):
        self.maxv = data
        if data:
            self._toggle_properties('maxv')
    def getmaxv(self):
        return self.maxv
    def resetmaxv(self):
        self.maxv = False

    def setshowtoggle(self, data):
        self.showToggleButton = data
    def getshowtoggle(self):
        return self.showToggleButton
    def resetshowtoggle(self):
        self.showToggleButton = True

    def setsettingmenu(self, data):
        self.showSettingMenu = data
    def getsettingmenu(self):
        return self.showSettingMenu
    def resetsettingmenu(self):
        self.showSettingMenu = True

    # Designer makes '\n' into '\\n' if added
    # we put fix it here so we actually get the newline
    def settexttemplate(self, data):
        data = data.replace('\\n' , '\n')
        self.texttemplate = data
    def gettexttemplate(self):
        return self.texttemplate
    def resettexttemplate(self):
        self.texttemplate = 'Value = %s'

    def setsteprate(self, data):
        self.step = data
    def getsteprate(self):
        return self.step
    def resetsteprate(self):
        self.steprate = 1

    rapid_rate = Property(bool, getrapid, setrapid, resetrapid)
    feed_rate = Property(bool, getfeed, setfeed, resetfeed)
    spindle_rate = Property(bool, getspindle, setspindle, resetspindle)
    jograte_rate = Property(bool, getjograte, setjograte, resetjograte)
    jograte_angular_rate = Property(bool, getjograteangular, setjograteangular, resetjograteangular)
    max_velocity_rate = Property(bool, getmaxv, setmaxv, resetmaxv)
    show_toggle_button = Property(bool, getshowtoggle, setshowtoggle, resetshowtoggle)
    show_setting_menu = Property(bool, getsettingmenu, setsettingmenu, resetsettingmenu)

    text_template = Property(str, gettexttemplate, settexttemplate, resettexttemplate)
    step_rate = Property(int, getsteprate, setsteprate, resetsteprate)

    ##############################
    # 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 = StatusAdjustmentBar()
    widget.PREFS_=None
    widget.buildWidget()
    widget.settexttemplate('Text %s')
    widget.setValue(75)
    widget.show()

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