"""Gazepoint GP3 / GP3 HD — open Gazepoint API over TCP (port 4242, XML records).

No vendor SDK needed: the GP3 server streams ASCII XML records over a socket. We
enable the data we want, then parse `<REC .../>` lines. Normalized screen coords
in [0,1]. (An LSL relay tool also exists; this driver is self-contained.)
"""

from __future__ import annotations

import socket

from ._base import DriverSource


class GazepointSource(DriverSource):
    HOST_DEFAULT = "127.0.0.1"
    PORT_DEFAULT = 4242

    def read(self):
        from pylsl import local_clock
        host = self.opts.get("host", self.HOST_DEFAULT)
        port = int(self.opts.get("port", self.PORT_DEFAULT))

        sock = socket.create_connection((host, port), timeout=5)
        sock.settimeout(0.5)
        for cmd in ('<SET ID="ENABLE_SEND_POG_BEST" STATE="1" />\r\n',
                    '<SET ID="ENABLE_SEND_PUPIL_LEFT" STATE="1" />\r\n',
                    '<SET ID="ENABLE_SEND_DATA" STATE="1" />\r\n'):
            sock.sendall(cmd.encode())

        buf = ""
        try:
            while not self.stopping:
                try:
                    chunk = sock.recv(4096).decode("ascii", "ignore")
                except socket.timeout:
                    continue
                if not chunk:
                    break
                buf += chunk
                while "\r\n" in buf:
                    line, buf = buf.split("\r\n", 1)
                    rec = _parse_rec(line)
                    if rec and "BPOGX" in rec:
                        x = float(rec.get("BPOGX", "nan"))
                        y = float(rec.get("BPOGY", "nan"))
                        pupil = float(rec.get("LPD", rec.get("LPMM", "0")) or 0)
                        valid = rec.get("BPOGV", "1") == "1"
                        if valid:
                            yield [x, y, pupil], local_clock()
        finally:
            sock.close()


def _parse_rec(line: str) -> dict | None:
    """Parse a Gazepoint '<REC KEY="val" ... />' line into a dict."""
    line = line.strip()
    if not line.startswith("<REC"):
        return None
    out = {}
    for tok in line[4:].rstrip("/>").split():
        if "=" in tok:
            k, _, v = tok.partition("=")
            out[k] = v.strip('"')
    return out
