"""
Reusable "illuminated indicator" binder for plain QLabels.

Creates two HAL bit pins per label -- <base_pin_name>.on and
<base_pin_name>.flash -- matching HalLedIndicator's own pin naming, so
swapping an existing HalLedIndicator for one of these labels needs no
postgui.hal changes, just a widget-type change in the .ui.

Usage, from any user_tabs/user_buttons wrapper .py file:

    from illuminated_label import IlluminatedLabelBinder
    ...
    self._indicators = [
        IlluminatedLabelBinder(my_qlabel, "sidebar_led-00", style="green", flash_style="red", font_size=40),
        IlluminatedLabelBinder(other_label, "hallabel-test1", style="yellow", flash_style="yellow"),
    ]
    # keep the returned objects referenced (e.g. in a list on self) --
    # if they get garbage collected the pin connections die with them.

Appearance is driven by indicator_label.qss (same folder, colors/borders/
padding only) via "indicatorStyle" (on-color theme, set once),
"indicatorFlashStyle" (flash-color theme, set once), and "indicatorState"
(live, changes with the pins). Font is set directly via QFont using
font_size (default 24pt) rather than the QSS, since setStyleSheet()
replaces the whole stylesheet and would silently override a "font:" line
in the QSS every time the state changes.
"""

import os
from PySide6.QtCore import QTimer
from PySide6.QtGui import QFont
from qtpyvcp import hal

_FLASH_INTERVAL_MS = 300
_qss_cache = None


def _load_qss():
    global _qss_cache
    if _qss_cache is None:
        qss_path = os.path.join(os.path.dirname(__file__), "indicator_label.qss")
        with open(qss_path, "r") as f:
            _qss_cache = f.read()
    return _qss_cache


class IlluminatedLabelBinder:

    _timer = None
    _instances = []

    def __init__(self, label, base_pin_name, style="green", flash_style="red", font_size=24):
        self.label = label
        self.label.setStyleSheet(_load_qss())
        # font is set directly via QFont, not the stylesheet -- setStyleSheet()
        # replaces the whole stylesheet text wholesale, so a "font:" line
        # inside the QSS would silently override this on every state change.
        # QFont is a separate mechanism and isn't touched by that.
        self.label.setFont(QFont("Probe Basic Bebas Mono", font_size))
        # both are fixed per label, set once -- only indicatorState changes live
        self.label.setProperty("indicatorStyle", style)
        self.label.setProperty("indicatorFlashStyle", flash_style)

        comp = hal.getComponent()
        self._on_pin = comp.addPin(base_pin_name + ".on", "bit", "in")
        self._flash_pin = comp.addPin(base_pin_name + ".flash", "bit", "in")

        self._on = bool(self._on_pin.value)
        self._flash = bool(self._flash_pin.value)
        self._blink_phase = False

        self._on_pin.valueChanged.connect(self._on_changed)
        self._flash_pin.valueChanged.connect(self._flash_changed)

        self._apply_state()

        IlluminatedLabelBinder._instances.append(self)
        IlluminatedLabelBinder._ensure_timer()

    def _on_changed(self, value):
        self._on = bool(value)
        self._apply_state()

    def _flash_changed(self, value):
        self._flash = bool(value)
        self._apply_state()

    def _apply_state(self):
        # flash takes priority over steady-on if both happen to be set
        if self._flash:
            state = "flash-on" if self._blink_phase else "flash-off"
        elif self._on:
            state = "on"
        else:
            state = "off"

        self.label.setProperty("indicatorState", state)
        # standard Qt idiom to force a QSS re-evaluation after a
        # property used in a selector changes
        self.label.style().unpolish(self.label)
        self.label.style().polish(self.label)

    @classmethod
    def _ensure_timer(cls):
        # one shared timer drives blinking for every bound label,
        # rather than a timer per label
        if cls._timer is None:
            cls._timer = QTimer()
            cls._timer.timeout.connect(cls._tick)
            cls._timer.start(_FLASH_INTERVAL_MS)

    @classmethod
    def _tick(cls):
        for inst in cls._instances:
            if inst._flash:
                inst._blink_phase = not inst._blink_phase
                inst._apply_state()
