#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Chip ID (TID / identidade do chip) — FastChecker II.
Usado pelo FastSurance (embutido no .exe) e pelo script ``Chip_ID.py`` na raiz do projeto.
"""

import argparse
import ctypes
import os
import sys
import threading
import time
import tkinter as tk
from tkinter import messagebox, ttk
from typing import Optional

from .i18n import get_translator, t

MODEL_LIBRARY = {
    0x1FE: ("CEITEC", "CTC13002"),
    0x1C0: ("Impinj", "Monza M780"),
    0x894: ("NXP", "UCODE 8"),
    0x093: ("Impinj", "Monza 3"),
    0x810: ("NXP", "UCODE 7"),
    0x160: ("Impinj", "Monza R6"),
    0x806: ("NXP", "G2iL"),
    0x003: ("NXP", "G2XM"),
    0x10C: ("Impinj", "Monza 4E"),
    0x100: ("Impinj", "Monza 4D"),
    0x105: ("Impinj", "Monza 4QT"),
    0x130: ("Impinj", "Monza 5"),
    0x412: ("Alien", "Higgs 3"),
}

MFG_DATABASE = {
    0x018: "CEITEC S.A.",
    0x003: "Impinj / Alien",
    0x801: "NXP",
    0x806: "NXP (Série UCODE)",
    0x006: "NXP (Série G2)",
}

RFID_CMD_SET_TXPOWER = 0x10
RFID_CMD_SET_FREQ_TABLE = 0x14
RFID_CMD_STOP_INVENTORY = 0x8C
RFID_CMD_INV_TAG = 0x80
RFID_CMD_READ_DATA = 0x84
MEM_TID = 0x02
BAUD_RATE = 115200


def get_dll():
    dll_name = "UHFRFID.dll"
    meipass = getattr(sys, "_MEIPASS", None)
    if meipass:
        bundled = os.path.join(meipass, dll_name)
        if os.path.isfile(bundled):
            return ctypes.CDLL(bundled)
    here = os.path.dirname(os.path.abspath(__file__))
    dev = os.path.join(here, dll_name)
    if os.path.isfile(dev):
        return ctypes.CDLL(dev)
    return ctypes.CDLL(dll_name)


class ChipIdFinal:
    """UI Chip ID: ``Tk`` isolado ou ``Toplevel`` quando ``master`` é a app principal."""

    def __init__(self, default_freq_mhz: float = 915.0, master: Optional[tk.Misc] = None):
        self._default_freq_mhz = float(default_freq_mhz)
        self._standalone = master is None
        self.translator = get_translator()
        self._widget_refs = {}
        self._field_labels = {}
        self._last_decode_raw = None
        self._status_done = False

        if self._standalone:
            self.root = tk.Tk()
        else:
            self.root = tk.Toplevel(master)
        self.root.geometry("640x580")

        try:
            self.sdk = get_dll()
            self.sdk.UHF_RFID_Open.argtypes = [ctypes.c_ubyte, ctypes.c_int]
            self.sdk.UHF_RFID_Set.argtypes = [
                ctypes.c_int,
                ctypes.c_char_p,
                ctypes.c_uint,
                ctypes.c_char_p,
                ctypes.POINTER(ctypes.c_uint),
            ]
        except Exception:
            messagebox.showerror(
                t("chip_id.dll_error_title"),
                t("chip_id.dll_error_msg"),
                parent=self.root,
            )

        frm = ttk.Frame(self.root, padding=10)
        frm.pack(fill=tk.BOTH, expand=True)

        cfg = ttk.LabelFrame(frm, padding=5)
        cfg.pack(fill=tk.X, pady=5)
        self._widget_refs["hw_frame"] = cfg

        lbl_com = ttk.Label(cfg)
        lbl_com.grid(row=0, column=0, padx=5, sticky=tk.W)
        self._widget_refs["lbl_com"] = lbl_com
        self.ent_com = ttk.Entry(cfg, width=10)
        self.ent_com.insert(0, "1")
        self.ent_com.grid(row=0, column=1, padx=5, pady=5)

        lbl_pwr = ttk.Label(cfg)
        lbl_pwr.grid(row=0, column=2, padx=5, sticky=tk.W)
        self._widget_refs["lbl_pwr"] = lbl_pwr
        self.ent_pwr = ttk.Entry(cfg, width=10)
        self.ent_pwr.insert(0, "20.0")
        self.ent_pwr.grid(row=0, column=3, padx=5, pady=5)

        lbl_freq = ttk.Label(cfg)
        lbl_freq.grid(row=1, column=0, padx=5, sticky=tk.W, pady=(0, 5))
        self._widget_refs["lbl_freq"] = lbl_freq
        self.ent_freq = ttk.Entry(cfg, width=10)
        self.ent_freq.insert(0, f"{self._default_freq_mhz:g}")
        self.ent_freq.grid(row=1, column=1, padx=5, pady=(0, 5), sticky=tk.W)

        self.btn_run = ttk.Button(frm, command=self.run_read)
        self.btn_run.pack(pady=10)
        self._widget_refs["btn_run"] = self.btn_run

        self.txt_tid = self.create_field(frm, "tid_raw", 1)
        self.txt_res = self.create_field(frm, "tid_tech", 2)
        self.txt_full = self.create_field(frm, "tid_clean", 1)

        self.lbl_st = ttk.Label(frm)
        self.lbl_st.pack(side=tk.BOTTOM, anchor=tk.W)
        self._widget_refs["lbl_st"] = self.lbl_st

        self._on_language_changed = self._make_language_handler()
        self.translator.add_language_change_listener(self._on_language_changed)
        self.root.protocol("WM_DELETE_WINDOW", self._on_close)

        self.refresh_language()

    def _make_language_handler(self):
        def _handler(old_language=None, new_language=None):
            try:
                if self.root.winfo_exists():
                    self.root.after(0, self.refresh_language)
            except tk.TclError:
                pass

        return _handler

    def _on_close(self):
        try:
            self.translator.remove_language_change_listener(self._on_language_changed)
        except Exception:
            pass
        self.root.destroy()

    def refresh_language(self):
        """Atualiza textos conforme PT/EN (mesmo tradutor que o FastChecker II)."""
        self.root.title(t("chip_id.window_title"))
        text_keys = {
            "hw_frame": "chip_id.hw_frame",
            "lbl_com": "chip_id.com_port",
            "lbl_pwr": "chip_id.power_dbm",
            "lbl_freq": "chip_id.freq_mhz",
            "btn_run": "chip_id.run_button",
        }
        for key, i18n_key in text_keys.items():
            w = self._widget_refs.get(key)
            if w is not None:
                w.config(text=t(i18n_key))
        st = self._widget_refs.get("lbl_st")
        if st is not None:
            st.config(
                text=t("chip_id.status_done") if self._status_done else t("chip_id.status_ready")
            )

        for field_key, label_widget in self._field_labels.items():
            label_widget.config(text=t(f"chip_id.{field_key}"))

        if self._last_decode_raw is not None:
            info, clean = self.decode_engine(self._last_decode_raw)
            self.update_text(self.txt_res, info)
            self.update_text(self.txt_full, clean)

    def create_field(self, parent, field_key: str, h: int):
        lbl = ttk.Label(parent)
        lbl.pack(anchor=tk.W)
        self._field_labels[field_key] = lbl
        text_widget = tk.Text(parent, height=h, state=tk.DISABLED, bg="#f0f0f0")
        text_widget.pack(fill=tk.X, pady=2)
        return text_widget

    def update_text(self, widget, text):
        widget.config(state=tk.NORMAL)
        widget.delete("1.0", tk.END)
        widget.insert(tk.END, text)
        widget.config(state=tk.DISABLED)

    def decode_engine(self, raw_hex: str):
        clean = raw_hex[8:]
        if not clean:
            return t("chip_id.decode_error"), ""
        val = int(clean[:8], 16)
        mdid_code = (val >> 12) & 0xFFF
        model_code = val & 0xFFF
        mfg_lbl = t("chip_id.manufacturer")
        model_lbl = t("chip_id.model")
        model_new_lbl = t("chip_id.model_new")

        if model_code in MODEL_LIBRARY:
            mfg, model = MODEL_LIBRARY[model_code]
            return f"{mfg_lbl}: {mfg}\n{model_lbl}: {model}", clean

        mfg = MFG_DATABASE.get(mdid_code, f"MDID: {hex(mdid_code)}")
        return f"{mfg_lbl}: {mfg}\n{model_new_lbl}: {hex(model_code)}", clean

    def run_read(self):
        for f in [self.txt_tid, self.txt_res, self.txt_full]:
            self.update_text(f, "")
        self._last_decode_raw = None
        self._status_done = False
        if "lbl_st" in self._widget_refs:
            self._widget_refs["lbl_st"].config(text=t("chip_id.status_ready"))
        try:
            com = int(self.ent_com.get())
            pwr = float(self.ent_pwr.get())
            freq_mhz = float(self.ent_freq.get().replace(",", "."))
        except ValueError:
            messagebox.showwarning(
                t("chip_id.warning_title"),
                t("chip_id.invalid_values"),
                parent=self.root,
            )
            return

        def worker():
            if self.sdk.UHF_RFID_Open(com, BAUD_RATE) == 0:
                out = ctypes.create_string_buffer(256)
                ln = ctypes.c_uint(0)

                self.sdk.UHF_RFID_Set(RFID_CMD_STOP_INVENTORY, None, 0, out, ctypes.byref(ln))

                p_val = int(pwr * 100)
                p_data = bytes([0x00, 0x00]) + p_val.to_bytes(2, "big") * 2
                self.sdk.UHF_RFID_Set(RFID_CMD_SET_TXPOWER, p_data, 6, out, ctypes.byref(ln))

                f_khz = int(round(freq_mhz * 1000))
                f_khz = max(800_000, min(1_000_000, f_khz))
                freq_data = b"\x01" + f_khz.to_bytes(3, "big")
                self.sdk.UHF_RFID_Set(RFID_CMD_SET_FREQ_TABLE, freq_data, 4, out, ctypes.byref(ln))

                self.sdk.UHF_RFID_Set(RFID_CMD_INV_TAG, b"\x00\x64", 2, out, ctypes.byref(ln))
                time.sleep(0.2)

                payload = (
                    b"\x00\x00\x00\x00"
                    + b"\x00\x00\x00\x00\x00"
                    + bytes([MEM_TID])
                    + b"\x00\x00\x00\x02"
                )
                self.sdk.UHF_RFID_Set(RFID_CMD_READ_DATA, payload, len(payload), out, ctypes.byref(ln))

                if ln.value >= 4:
                    raw = out.raw[: ln.value].hex().upper()
                    if not raw.startswith("0003"):
                        self._last_decode_raw = raw
                        self.root.after(0, lambda r=raw: self.update_text(self.txt_tid, r))
                        info, clean = self.decode_engine(raw)
                        self.root.after(0, lambda i=info: self.update_text(self.txt_res, i))
                        self.root.after(0, lambda c=clean: self.update_text(self.txt_full, c))
                    else:
                        self.root.after(
                            0,
                            lambda: self.update_text(
                                self.txt_res,
                                t("chip_id.read_error_0003"),
                            ),
                        )

                self.sdk.UHF_RFID_Close(com)
            if self.root.winfo_exists():
                def _set_done():
                    self._status_done = True
                    self.lbl_st.config(text=t("chip_id.status_done"))

                self.root.after(0, _set_done)

        threading.Thread(target=worker, daemon=True).start()


def main_cli() -> None:
    parser = argparse.ArgumentParser(description="Chip_ID — identidade do chip (TID) via UHFRFID.dll")
    parser.add_argument(
        "--freq-mhz",
        type=float,
        default=915.0,
        help="Frequência do teste em MHz (ex.: 915 FCC/Anatel/broadband; 866 ETSI).",
    )
    args, _unknown = parser.parse_known_args()
    app = ChipIdFinal(default_freq_mhz=args.freq_mhz)
    app.root.mainloop()


if __name__ == "__main__":
    main_cli()
