# SPDX-License-Identifier: Apache-2.0
"""ST25DV64KC dynamic NFC tag over I2C (SparkFun Qwiic Dynamic NFC/RFID Tag).

ADR-0015: the tag is always readable. The device writes the meeting's note when the meeting ends, and RF is
switched off only for the moment of the write, so a phone can never read half a note.

Register addresses below follow ST's ST25DV-I2C datasheet as recalled; **verify against DS13519 before first
use** (marked VERIFY). The bus is any hw.I2c (hw_linux.LinuxI2c on the board, hw.SimI2c in tests).
"""
from __future__ import annotations

import time

USER_ADDR = 0x53          # user memory + dynamic registers (VERIFY)
RF_MNGT_DYN = 0x2003      # bit0 RF_DISABLE, bit1 RF_SLEEP (VERIFY)
PAGE = 4                  # conservative write chunk; EEPROM write ~5 ms per page (VERIFY: 256-byte sequential writes)
WRITE_S = 0.006


class ST25DV:
    def __init__(self, i2c=None, sleep=time.sleep):
        if i2c is None:
            from .hw_linux import LinuxI2c   # imported lazily so tests run off-device
            i2c = LinuxI2c()
        self.i2c, self.sleep = i2c, sleep

    def _write(self, mem: int, data: bytes) -> None:
        self.i2c.transfer(USER_ADDR, mem.to_bytes(2, "big") + data)

    def _read(self, mem: int, n: int) -> bytes:
        return self.i2c.transfer(USER_ADDR, mem.to_bytes(2, "big"), n)

    def rf(self, enabled: bool) -> None:
        self._write(RF_MNGT_DYN, bytes([0x00 if enabled else 0x01]))

    def write_image(self, image: bytes) -> None:
        for off in range(0, len(image), PAGE):
            self._write(off, image[off:off + PAGE])
            self.sleep(WRITE_S)
        if self._read(0, len(image)) != image:
            raise IOError("tag verify failed")

    def wipe(self, n: int) -> None:
        self.write_image(b"\x00" * n)


def publish(tag: ST25DV, image: bytes) -> None:
    """ADR-0015: write the note with RF off, verify it, then leave RF on so any phone touching the tail can read it."""
    tag.rf(False)
    try:
        tag.write_image(image)
    finally:
        tag.rf(True)


def handoff(tag: ST25DV, image: bytes, window_s: float = 10.0, button_held=lambda: True) -> None:
    """Superseded by publish() (ADR-0015 removed the hold-to-arm hand-off of ADR-0013). Kept for reference:
    write, open RF while the eye is held (max window_s), then close and wipe."""
    tag.rf(False)
    tag.write_image(image)
    tag.rf(True)
    t0 = time.monotonic()
    try:
        while button_held() and time.monotonic() - t0 < window_s:
            time.sleep(0.05)
    finally:
        tag.rf(False)
        tag.wipe(len(image))        # nothing stays readable on the tag
