"""Read-only telemetry measurement display."""
from textual.widgets import Static

from ..protocol import TelemetryData


class TelemetryPanel(Static):
    """Displays all telemetry values."""

    DEFAULT_CSS = """
    TelemetryPanel {
        width: 100%;
        height: auto;
        min-height: 20;
        padding: 1;
    }
    """

    ALPHA = 0.05  # EMA filter coefficient

    MASTER_TICKS_MID = 7158  # s = 2*last_tmp - MASTER_TICKS_MID (edge-separation abscissa)
    # dead-time is a single static value now (dt_normal, 0x60); track it for display
    DT_IDS = {0x60}
    DT_DEFAULTS = {0x60: 20}

    def __init__(self):
        super().__init__("Waiting for telemetry...")
        self._data: TelemetryData | None = None
        self._vin_f: float = 0.0
        self._vout_f: float = 0.0
        self._iin_f: float = 0.0
        self._iout_f: float = 0.0
        self._vfly_f: float = 0.0
        self._seeded: bool = False
        self.filter_enabled: bool = True
        self._dt_params: dict[int, float] = dict(self.DT_DEFAULTS)
        # Link stats, set by the app's 0.5 s status timer
        self.link_rx: float = 0.0
        self.link_tx: float | None = None
        self.link_loss: float = 0.0

    def update_telemetry(self, t: TelemetryData):
        self._data = t

        # EMA filter on V/I
        if not self._seeded:
            self._vin_f = t.vin
            self._vout_f = t.vout
            self._iin_f = t.iin
            self._iout_f = t.iout
            self._vfly_f = t.vfly
            self._seeded = True
        else:
            a = self.ALPHA
            self._vin_f += a * (t.vin - self._vin_f)
            self._vout_f += a * (t.vout - self._vout_f)
            self._iin_f += a * (t.iin - self._iin_f)
            self._iout_f += a * (t.iout - self._iout_f)
            self._vfly_f += a * (t.vfly - self._vfly_f)

        # Compute power and efficiency from filtered or raw values
        if self.filter_enabled:
            v_in, v_out, i_in, i_out = self._vin_f, self._vout_f, self._iin_f, self._iout_f
        else:
            v_in, v_out, i_in, i_out = t.vin, t.vout, t.iin, t.iout
        p_in = v_in * (-i_in) / 1e6
        p_out = v_out * i_out / 1e6
        if p_in > 0.1:
            eta = p_out / p_in * 100.0
        else:
            eta = 0.0

        # Dead-time is a single static value (dt_normal), applied to both edges/timers.
        active_dt = self._dt_params.get(0x60, 20)

        if self.link_tx is not None:
            if self.link_loss >= 5.0:
                loss_s = f"[red]{self.link_loss:.0f}%[/red]"
            else:
                loss_s = f"{self.link_loss:.0f}%"
            link_lines = [
                f"  link rx   : {self.link_rx:8.1f} Hz",
                f"  link tx   : {self.link_tx:8.1f} Hz",
                f"  link loss : {loss_s}",
            ]
        else:
            link_lines = ["  link      : measuring..."]

        lines = [
            "[bold]MEASUREMENTS[/bold]",
            *link_lines,
            "",
            f"  vin       : {self._vin_f if self.filter_enabled else t.vin:8.0f} mV",
            f"  vout      : {self._vout_f if self.filter_enabled else t.vout:8.0f} mV",
            f"  iin       : {self._iin_f if self.filter_enabled else t.iin:8.0f} mA",
            f"  iout      : {self._iout_f if self.filter_enabled else t.iout:8.0f} mA",
            f"  P_IN      : {p_in:8.2f} W",
            f"  P_OUT     : {p_out:8.2f} W",
            f"  EFF       : {eta:8.1f} %" if p_in > 0.1 else "  EFF       :      --- %",
            f"  vfly      : {self._vfly_f if self.filter_enabled else t.vfly:8.0f} mV",
            f"  etemp     : {t.etemp:8.1f} C",
            f"  btemp     : {t.btemp:8.1f} C",
            f"  deadtime  : {active_dt:8.1f} ticks",
            f"            : {active_dt / 1.36:8.1f} ns",
            "",
            f"  last_tmp  : {t.last_tmp:8d}",
            f"  s         : {2 * t.last_tmp - self.MASTER_TICKS_MID:+8d}  ({'D>0.5' if 2 * t.last_tmp >= self.MASTER_TICKS_MID else 'D<0.5'})",
            f"  VREF      : {t.VREF:8d}",
            f"  vfly_corr : {t.vfly_correction:8d}",
            f"  cmp_outer : {t.cmp_outer:8d}  (F: T1/T4)",
            f"  cmp_inner : {t.cmp_inner:8d}  (E: T2/T3)",
            f"  cmp_diff  : {t.cmp_outer - t.cmp_inner:+8d}",
            f"  phase_ofs : {t.vfly_ofs_applied:+8d}  (applied)",
            "",
            f"  vfly_int  : {t.vfly_integral:10.3f}",
            f"  vfly_avg  : {t.vfly_avg_debug:10.1f}",
            f"  cc.out_f  : {t.cc_output_f:10.1f}",
            f"  mppt.duty : {t.mppt_iref:8.0f}",
            f"  mppt.power: {t.mppt_last_vin:12.0f}",
            f"  mppt.dir  : {t.mppt_last_iin:4.0f}",
        ]
        self.update("\n".join(lines))

    def update_dt_param(self, param_id: int, value: float):
        if param_id in self.DT_IDS:
            self._dt_params[param_id] = value
