#!/usr/bin/env python3

#TODO: """DocString if there is one"""

import epyqlib.widgets.abstractwidget
import os
import re

from PyQt5 import QtCore
from PyQt5.QtCore import pyqtProperty, QFile, QFileInfo, QEvent
from PyQt5.QtGui import QColor
from PyQt5.QtXml import QDomDocument

# See file COPYING in this source tree
__copyright__ = 'Copyright 2016, EPC Power Corp.'
__license__ = 'GPLv2+'


darker_factor = 400


class FontChangeEventSignal(QtCore.QObject):
    triggered = QtCore.pyqtSignal()

    def eventFilter(self, _, event):
        if event.type() == QtCore.QEvent.FontChange:
            self.triggered.emit()

        return False


def set_attribute_recursive(element, tag_name, attribute,
                            new_color):
    if element.tagName() == tag_name:
        text = element.attribute(attribute)
        text = re.sub('(?<=fill:#)[0-9A-F]{6}', new_color, text)
        element.setAttribute(attribute, text)

    child_nodes = element.childNodes()
    children = [child_nodes.at(i) for i in range(child_nodes.length())]
    for child in children:
        if child.isElement():
            set_attribute_recursive(element=child.toElement(),
                                    tag_name=tag_name,
                                    attribute=attribute,
                                    new_color=new_color)


def make_color(svg_string, new_color):
    doc = QDomDocument()
    doc.setContent(svg_string)
    set_attribute_recursive(element=doc.documentElement(),
                            tag_name="circle",
                            attribute="style",
                            new_color=new_color)

    return doc.toByteArray()


def rgb_string(color):
    return ('{:02X}' * 3).format(*color.getRgb())


class Led(epyqlib.widgets.abstractwidget.AbstractWidget):
    def __init__(self, parent=None, in_designer=False):
        ui_file = os.path.join(QFileInfo.absolutePath(QFileInfo(__file__)),
                               'led.ui')

        epyqlib.widgets.abstractwidget.AbstractWidget.__init__(self,
                ui=ui_file, parent=parent, in_designer=in_designer)

        file_name = 'led.svg'

        # TODO: CAMPid 9549757292917394095482739548437597676742
        if not QFileInfo(file_name).isAbsolute():
            file = os.path.join(
                QFileInfo.absolutePath(QFileInfo(__file__)), file_name)
        else:
            file = file_name
        file = QFile(file)
        file.open(QFile.ReadOnly | QFile.Text)
        self.svg_string = file.readAll()

        # TODO: shouldn't this be in AbstractWidget?
        self._on_value = 1
        self._relative_height = 1
        self._label_from_enumeration = False

        self._value = False
        self.svg = {
            'on': None,
            'automatic_off': None,
            'manual_off': None
        }
        self.ui.value.main_element = 'led'

        self._last_loaded_svg = object()

        self._on_color = QColor()
        self._manual_off_color = QColor()
        self._automatic_off_color = True
        self.on_color = QColor("#20C020")
        self.manual_off_color = self.on_color.darker(factor=darker_factor)

        self.update_svg()

        self.font_event_signal = FontChangeEventSignal()
        self.ui.label.installEventFilter(self.font_event_signal)
        self.font_event_signal.triggered.connect(self.update_svg)

    @pyqtProperty(bool)
    def label_from_enumeration(self):
        return self._label_from_enumeration

    @label_from_enumeration.setter
    def label_from_enumeration(self, from_enumeration):
        self._label_from_enumeration =  bool(from_enumeration)

        # TODO: this is a hacky way to trigger an update
        self.set_signal(signal=self.signal_object,
                        force_update=True)

    @pyqtProperty(int)
    def on_value(self):
        return self._on_value

    @on_value.setter
    def on_value(self, new_on_value):
        new_on_value = int(new_on_value)
        if self._on_value != new_on_value:
            self._on_value = new_on_value
            self.update_svg()

            # TODO: this is a hacky way to trigger an update
            self.set_signal(signal=self.signal_object,
                            force_update=True)

    @pyqtProperty(bool)
    def automatic_off_color(self):
        return self._automatic_off_color

    @automatic_off_color.setter
    def automatic_off_color(self, automatic):
        automatic = bool(automatic)
        if automatic != self._automatic_off_color:
            self._automatic_off_color = bool(automatic)
            self.update_svg()

    @pyqtProperty(QColor)
    def on_color(self):
        return self._on_color

    @on_color.setter
    def on_color(self, new_color):
        self._on_color = QColor(new_color)

        self.svg['on'] = make_color(self.svg_string, rgb_string(self._on_color))

        if self.automatic_off_color:
            self.svg['automatic_off'] = make_color(
                self.svg_string,
                rgb_string(self._on_color.darker(factor=darker_factor))
            )

        self.update_svg()

    @pyqtProperty(QColor)
    def manual_off_color(self):
        return self._manual_off_color

    @manual_off_color.setter
    def manual_off_color(self, new_color):
        self._manual_off_color = QColor(new_color)

        self.svg['manual_off'] = make_color(self.svg_string, rgb_string(self._manual_off_color))

        self.update_svg()

    @pyqtProperty(float)
    def relative_height(self):
        return self._relative_height

    @relative_height.setter
    def relative_height(self, multiplier):
        self._relative_height = multiplier

        self.update_svg()

    def set_value(self, value):
        # TODO: quit hardcoding this and it's better implemented elsewhere
        if self.signal_object is not None:
            value = self.signal_object.value
        else:
            value = value

        self._value = (value == self.on_value)

        self.update_svg()

    def update_svg(self):
        if self._value:
            svg = self.svg['on']
        elif self.automatic_off_color:
            svg = self.svg['automatic_off']
        else:
            svg = self.svg['manual_off']

        if svg is not self._last_loaded_svg:
            self.ui.value.load(svg)
            self._last_loaded_svg = svg

        # TODO: figure out if this should be needed.  seems not but
        #       without it the font metric can return invalid values
        #       at least when the label_visible property is not
        #       explicitly set in the .ui file and the font size
        #       has been set via a second stylesheet.
        #
        #       Possibly:
        #           https://bugreports.qt.io/browse/QTBUG-42720

        self.ui.label.setVisible(self.ui.label.isVisibleTo(self))
        height = self.relative_height * self.ui.label.fontMetrics().height()

        ratio = self.ui.value.ratio()
        if ratio != 0:
            width = height / ratio
        else:
            width = 0

        self.ui.value.setFixedSize(width, height)

    def set_label_custom(self, new_signal=None):
        label = None

        if new_signal is not None:
            if self.label_from_enumeration and new_signal is not None:
                label = new_signal.enumeration[self.on_value]

        return label

    def event(self, *args, **kwargs):
        result = epyqlib.widgets.abstractwidget.AbstractWidget.event(
            self, *args, **kwargs
        )

        event = args[0]
        if event.type() == QEvent.Polish:
            self.update_svg()

        return result

    def set_unit_text(self, units):
        pass


if __name__ == '__main__':
    import sys

    print('No script functionality here')
    sys.exit(1)     # non-zero is a failure
