import os.path
import sys
import weakref

from PyQt5 import QtSvg
from PyQt5.QtCore import Qt

from lib.helpers import timing
from PyQt5.QtCore import QTimer
from PyQt5.QtGui import QKeySequence, QPixmap
from PyQt5.QtWidgets import QShortcut, QApplication, QVBoxLayout
from ui.test_test import Test_Test
from lxml import etree


class Test_Instructions(Test_Test):
    def __init__(self,config ,components=None, recipe=None, step=None, run_once=False, reset_on_start=False, enable_override=True,bench_name="generic"):
        super().__init__(components=components, recipe=recipe, step=step, run_once=run_once, reset_on_start=reset_on_start, enable_override=enable_override)
        self.get_connection = None
        self.bench_name = bench_name
        self.config = config
        self.recipe=recipe
        self.svgWidget=None
        self.svg_root=None
        self.flag=False
        self.inputs={}
        self.svg_widget=QtSvg.QSvgWidget()
        self.layout = QVBoxLayout()
        self.layout.addWidget(self.svg_widget)
        self.svg_w.setLayout(self.layout)
        self.svg_path=os.path.join("config","instruction_images",self.config["machine"].get("instruction_folder","generic"),"")
        self.timer = QTimer()
        self.timer.timeout.connect(self.toggle_icons)
        self.expected_input_state=True

        if self.config["hardware_config"]["tecna_t3"] == "absent" and self.config["hardware_config"]["furness_controls"] == "absent":
            if hasattr(self, "print_b"):
                self.print_b.setVisible(True)
                self.print_b.setEnabled(True)
                self.print_b.clicked.connect(self.print_button)

    def start(self, recipe=None, step=None, pieces=None):
        show = super().start(recipe=recipe, step=step)

        self.timer.start(1000)
        # setup test loop
        if "digital_io" in self.components.keys():
            self.get_connection = self.components["digital_io"].out.connect(self.get)
        svg_path = f"{self.svg_path}{self.recipe}.svg"
        if step is not None:
            if step.step_type == "instruction":
                svg_path = f"{self.svg_path}{self.recipe}.svg"
            if step.step_type == "instruction_extra":
                svg_path = f"{self.svg_path}{self.recipe}-extra.svg"

            if not os.path.exists(svg_path):
                svg_path=f"{self.svg_path}DEFAULT.svg"
            self.svg_root = etree.parse(svg_path)
            self.svg_str = etree.tostring(self.svg_root)
            self.svg_str=etree.tostring(self.svg_root)
            self.expected_input_state = True if step.step_type == "instruction" or "instruction_extra" else False

        self.monitored_ids=self.svg_root.xpath(f'''.//*[starts-with(@id, 'sensor_')]''')
        self.warning_ids=self.svg_root.xpath(f'''.//*[starts-with(@id, 'warning_')]''')
        self.tape_ids=self.svg_root.xpath(f'''.//*[starts-with(@id, 'tape_')]''')

        self.done = False
        return show

    def toggle_icons(self, step=None):
        self.flag = not self.flag

        # Handle tape_ids
        for elem in self.tape_ids:
            if self.flag:
                self.show_tape(elem)
            else:
                self.hide_tape(elem)

        for elem in self.warning_ids:
            warning_name = elem.attrib['id']
            warning_index_part = warning_name.split("_")[1]

            try:
                warning_index = int(warning_index_part) - 3
            except ValueError:
                warning_index = warning_index_part

            if warning_index not in self.inputs:
                self.inputs[warning_index] = False

            if self.inputs[warning_index]:
                self.show_ok(elem)
                self.show_icon(elem)
            else:
                self.show_warning_ko(elem)
                if self.flag:
                    self.show_icon(elem)
                else:
                    self.hide_icon(elem)

        for elem in self.monitored_ids:
            sensor_name = elem.attrib['id']
            sensor_index_part = sensor_name.split("_")[1]

            try:
                sensor_index = int(sensor_index_part) - 3
            except ValueError:
                sensor_index = sensor_index_part

            inverse = '_inv' in sensor_name
            stat = '_stat' in sensor_name

            if sensor_index not in self.inputs:
                self.inputs[sensor_index] = False
            if stat:
                if inverse and not self.inputs[sensor_index]:
                    self.show_ok(elem)
                    self.show_icon(elem)
                else:
                    self.show_ok(elem)
                    self.show_icon(elem)
            else:
                if inverse:
                    if not self.inputs[sensor_index]:
                        self.show_ko(elem)
                        self.show_icon(elem)
                    else:
                        self.show_ok(elem)
                        if self.flag:
                            self.show_icon(elem)
                        else:
                            self.hide_icon(elem)
                else:
                    if self.inputs[sensor_index]:
                        self.show_ok(elem)
                        self.show_icon(elem)
                    else:
                        self.show_ko(elem)
                        if self.flag:
                            self.show_icon(elem)
                        else:
                            self.hide_icon(elem)

        self.show_svg()
    def print_button(self):
        super().get([{
            "time": timing(),
            "results": {
                "ok": True,
            },
        }], override=False, fail=False, skip_delay=False)

        self.set_done()

        self.toggle_icons()

    def show_ok(self,id):
        id.attrib["{http://www.w3.org/1999/xlink}href"]= f"{self.svg_path}img/ok.png"
    def show_ko(self,id):
        id.attrib["{http://www.w3.org/1999/xlink}href"]= f"{self.svg_path}img/arw-yel-down.png"

    def show_icon(self,id):
        id.set("display", "inline")

    def hide_icon(self,id):
            id.set("display", "none")

    def show_warning_ko(self,id):
        id.attrib["{http://www.w3.org/1999/xlink}href"]= f"{self.svg_path}img/warning.png"

    def show_tape(self,id):
        id.attrib["{http://www.w3.org/1999/xlink}href"]= f"{self.svg_path}img/tape_black.png"
        id.set("display", "inline")

    def hide_tape(self,id):
        id.set("display", "none")

    def show_svg(self):
        self.svg_str = etree.tostring(self.svg_root)
        self.svg_widget.load(self.svg_str)
        QApplication.processEvents()

    def get(self, data=None, override=False):
        if self.parent_assembly_widget().parent().step.step_type == "select_recipe":
            self.stop()
            return
        if self.done:  # avoid proccessing if completed
            return
        if data is None:
            super().get(None, override=override)
            return

        #check if all sensor are ok
        ok = True
        if len(data[0]["digital_io"])==0:
            return
        for sensor in self.monitored_ids:
            sensor_name = sensor.attrib['id']
            sensor_index_part = sensor.attrib['id'].split("_")[1]

            try:
                sensor_index = int(sensor_index_part) - 3
            except ValueError:
                sensor_index = sensor_index_part

            inverse = 'inv' in sensor_name
            byte_idx=int(sensor_index/8) if isinstance(sensor_index, int) else None
            bit_idx=sensor_index%8 if isinstance(sensor_index, int) else None
            current_bitstate = data[0]["digital_io"][byte_idx][bit_idx] if isinstance(sensor_index, int) else None

            if 'stat' in sensor_name:
                continue

            if isinstance(sensor_index, int):
                if (inverse and current_bitstate != self.expected_input_state) or (not inverse and current_bitstate == self.expected_input_state):
                    self.inputs[sensor_index] = True
                else:
                    self.inputs[sensor_index] = False
                    ok = False
            else:
                self.inputs[sensor_index] = False
                ok = False

        if ok:
            self.stop()
            super().get([{
                "time": timing(),
                "results": {
                    "ok": ok,
                },
            }], override=override, fail=ok is False, skip_delay=False)
            self.set_done()

            self.toggle_icons()


    def set_done(self):
        self.done=True
        self.done_ok=True


    def stop(self):
        #self.done=True
        #self.done_ok = True
        self.timer.stop()
        if self.get_connection is not None:
            self.disconnect(self.get_connection)
        super().stop()

    def reset(self):
        super().reset()
