# -*- coding: utf-8 -*-
from __future__ import annotations

import math
import time

from pyqtgraph import functions as fn  # type: ignore
from qtpy.QtGui import QPaintEvent
from qtpy.QtWidgets import QLabel, QWidget

__all__ = ["ValueLabel"]


class ValueLabel(QLabel):
    """
    QLabel specifically for displaying numerical values.
    Extends QLabel adding some extra functionality:

    - displaying units with si prefix
    - built-in exponential averaging

    This is ValueLabel from pyqtgraph made right.
    """

    values: list[tuple[float, int | float]]
    averageTime: float
    suffix: str
    siPrefix: bool
    decimals: int

    def __init__(
        self,
        parent: QWidget | None = None,
        suffix: str | None = None,
        siPrefix: bool = False,
        decimals: int = 3,
        averageTime: float = 0.0,
        formatStr: str | None = None,
    ) -> None:
        """
        ==============      ==================================================================================
        **Arguments:**
        suffix              (str or None) The suffix to place after the value
        siPrefix            (bool) Whether to add an SI prefix to the units and display a scaled value
        averageTime         (float) The length of time in seconds to average values. If this value
                            is 0, then no averaging is performed. As this value increases
                            the display value will appear to change more slowly and smoothly.
        formatStr           (str) Optionally, provide a format string to use when displaying text. The text
                            will be generated by calling `formatStr.format(value=, avgValue=, suffix=)`
                            (see Python documentation on `str.format`)
                            This option is not compatible with siPrefix
        ==============      ==================================================================================
        """
        QLabel.__init__(self, parent)
        self.values = []
        self.averageTime = averageTime  # no averaging by default
        self.suffix = suffix or ""
        self.siPrefix = siPrefix
        self.decimals = decimals
        if formatStr is None:
            self.formatStr = "{scaledValue:.{decimals}f}{suffixGap}{siPrefix}{suffix}"
        else:
            self.formatStr = formatStr

    def setValue(self, value: int | float) -> None:
        now: float = time.monotonic()
        self.values.append((now, value))
        cutoff: float = now - self.averageTime
        while len(self.values) > 0 and self.values[0][0] < cutoff:
            self.values.pop(0)
        self.update()

    def setFormatStr(self, text: str) -> None:
        self.formatStr = text
        self.update()

    def setAverageTime(self, t: float) -> None:
        self.averageTime = t

    def averageValue(self) -> float:
        if self.values:
            return sum(v[1] for v in self.values) / float(len(self.values))
        else:
            return math.nan

    def paintEvent(self, ev: QPaintEvent) -> None:
        self.setText(self.generateText())
        return QLabel.paintEvent(self, ev)

    def generateText(self) -> str:
        if len(self.values) == 0:
            return ""
        val: float = self.averageValue()
        if math.isnan(val):
            return ""

        # format the string
        parts = {"value": val, "suffix": self.suffix, "decimals": self.decimals}
        if self.siPrefix and self.suffix:
            # SI prefix was requested, so scale the value accordingly
            (s, p) = fn.siScale(val)
            parts.update({"siPrefix": p, "scaledValue": s * val})
        else:
            # no SI prefix /suffix requested; scale is 1
            exp: int = int(math.floor(math.log10(abs(val)))) if val != 0.0 else 0
            man: float = val * math.pow(0.1, exp)
            parts.update(
                {"siPrefix": "", "scaledValue": val, "exp": exp, "mantissa": man}
            )

        parts["suffixGap"] = " " if (parts["suffix"] or parts["siPrefix"]) else ""

        return self.formatStr.format(**parts).replace("-", "−")
