# plugins/legacy_mbm.py
import os, csv, struct
import datetime as dt
from typing import Any, List, Callable, Optional

NAME = "legacy_mbm"

# konfigurace pluginu (klidně později načítat z ini)
IP = "192.168.11.155"
PORT = 503
UNIT = 1

FIRST_INSTR = 1
FIRST_ADDR = 0
FLOAT_COUNT_NEEDED = 12
WORD_SWAP = False

SECOND_INSTR = 2
SECOND_ADDR = 16384
DI_COUNT_NEEDED = 12

REL_TOL = 0.33
ABS_FLOOR = 2.0
RANGES = [(0.0, 120.0)] * 6 + [(200.0, 250.0)] * 6

def regs_to_floats(registers: List[int], word_swap: bool = False) -> List[float]:
    if len(registers) % 2 != 0:
        registers = registers[:-1]
    out = []
    for i in range(0, len(registers), 2):
        w1, w2 = registers[i], registers[i+1]
        if word_swap:
            w1, w2 = w2, w1
        b = struct.pack(">H", int(w1) & 0xFFFF) + struct.pack(">H", int(w2) & 0xFFFF)
        out.append(struct.unpack(">f", b)[0])
    return out

def round2(vals: List[float]) -> List[float]:
    return [round(float(v), 2) for v in vals]

def clamp(v: float, lo: float, hi: float) -> float:
    return lo if v < lo else hi if v > hi else v

def within(v: float, ref: float) -> bool:
    tol = max(abs(ref) * REL_TOL, ABS_FLOOR)
    return abs(v - ref) <= tol

def pad_or_trim(lst: List[Any], n: int, pad: Any = 0) -> List[Any]:
    return lst[:n] if len(lst) >= n else lst + [pad] * (n - len(lst))

def apply_ranges(vals: List[float]) -> List[float]:
    out = []
    for i, v in enumerate(vals, start=1):
        if i <= len(RANGES):
            lo, hi = RANGES[i-1]
            out.append(clamp(v, lo, hi))
        else:
            out.append(v)
    return out

def triple_read_with_validation(reader: Callable[[], List[float]]) -> List[float]:
    A = reader()
    B = reader()

    need_third = any(not within(B[i], A[i]) for i in range(min(len(A), len(B))))
    C: Optional[List[float]] = reader() if need_third else None

    result = []
    n = min(len(A), len(B)) if C is None else min(len(A), len(B), len(C))
    for i in range(n):
        a, b = A[i], B[i]
        if C is None:
            result.append(b)
            continue
        c = C[i]
        if within(b, a):
            result.append(b)
        elif within(c, a):
            result.append(a)
        elif within(c, b):
            result.append(b)
        else:
            result.append(b)
    return pad_or_trim(result, len(A), 0.0)

class LegacyPlugin:
    name = NAME

    def on_tick(self, tick: dt.datetime, api, log_root: str) -> None:
        # vlastní soubor do podsložky
        folder = os.path.join(log_root, "LEGACY")
        os.makedirs(folder, exist_ok=True)
        fn = os.path.join(folder, tick.strftime("%d_%m_%y.csv"))

        def floats_reader():
            regs = api.read_raw(IP, PORT, UNIT, FIRST_INSTR, FIRST_ADDR, max(2*FLOAT_COUNT_NEEDED, 2))
            vals = regs_to_floats(regs, word_swap=WORD_SWAP)
            vals = round2(vals)
            return pad_or_trim(vals, FLOAT_COUNT_NEEDED, 0.0)

        floats = triple_read_with_validation(floats_reader)
        floats = apply_ranges(floats)
        floats = round2(floats)

        di_raw = api.read_raw(IP, PORT, UNIT, SECOND_INSTR, SECOND_ADDR, DI_COUNT_NEEDED)
        di = [1 if bool(x) else 0 for x in di_raw]
        di = pad_or_trim(di, DI_COUNT_NEEDED, 0)

        row = [tick.strftime("%H:%M")] + floats[:FLOAT_COUNT_NEEDED] + di[:DI_COUNT_NEEDED]

        with open(fn, "a", newline="", encoding="utf-8") as f:
            csv.writer(f, delimiter=",").writerow(row)

plugin = LegacyPlugin()

