#!/usr/bin/env python3
"""Stuurt de snoezel-presets als Art-Net naar de DMX-node.

Draait op een computer of Raspberry Pi in hetzelfde wifi-netwerk als de node.
De website bepaalt welke preset aan staat. Een Flic-knop zet de reeks aan of uit.
"""

import ipaddress
import json
import socket
import ssl
import time
import urllib.request
from pathlib import Path

ROOT = Path(__file__).resolve().parent
CONFIG = ROOT / "config.json"


def load_config():
    if not CONFIG.is_file():
        raise SystemExit(
            "Geen bridge/config.json. Kopieer config.example.json en vul token en het IP van de Art-Net-node in."
        )
    cfg = json.loads(CONFIG.read_text())
    node = str(cfg.get("node") or "").strip()
    token = str(cfg.get("token") or "").strip()
    try:
        ipaddress.ip_address(node)
    except ValueError:
        raise SystemExit("Zet in config.json het IP-adres van de Art-Net-node, bijvoorbeeld 192.168.1.50.")
    if not token or token.startswith("PLAK"):
        raise SystemExit("Zet in config.json het token van de snoezelpagina.")
    cfg["server"] = str(cfg.get("server") or "https://dmx.kloek-advies.nl").rstrip("/")
    cfg["universe"] = int(cfg.get("universe") or 0)
    cfg["fps"] = max(5, min(40, int(cfg.get("fps") or 20)))
    return cfg


def artnet_packet(channels, universe, sequence):
    data = bytes(max(0, min(255, int(v))) for v in channels[:512])
    if len(data) < 512:
        data += bytes(512 - len(data))
    length = len(data)
    return (
        b"Art-Net\x00"
        + (0x5000).to_bytes(2, "little")
        + (14).to_bytes(2, "big")
        + bytes([sequence & 255, 0])
        + int(universe).to_bytes(2, "little")
        + length.to_bytes(2, "big")
        + data
    )


def fetch_frame(cfg):
    url = cfg["server"] + "/api/frame.php?token=" + cfg["token"]
    req = urllib.request.Request(url, headers={"Accept": "application/json"})
    context = ssl.create_default_context()
    try:
        with urllib.request.urlopen(req, timeout=4, context=context) as res:
            return json.loads(res.read().decode())
    except ssl.SSLError:
        context = ssl._create_unverified_context()
        with urllib.request.urlopen(req, timeout=4, context=context) as res:
            return json.loads(res.read().decode())


def main():
    cfg = load_config()
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    dest = (cfg["node"], 6454)
    period = 1 / cfg["fps"]
    channels = [0] * 512
    sequence = 1
    next_poll = 0
    name = ""
    print(f"Art-Net naar {dest[0]}:{dest[1]}, universum {cfg['universe']}. Stoppen met Ctrl+C.")
    while True:
        now = time.monotonic()
        if now >= next_poll:
            next_poll = now + 0.4
            try:
                frame = fetch_frame(cfg)
                channels = frame.get("channels") or channels
                label = frame.get("name") or ""
                if label != name:
                    name = label
                    state = "aan" if frame.get("on") else "uit"
                    print(f"{state}: {name}")
            except Exception as exc:
                print(f"Website niet bereikt: {exc}")
        sock.sendto(artnet_packet(channels, cfg["universe"], sequence), dest)
        sequence = (sequence + 1) % 256 or 1
        time.sleep(period)


if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\nGestopt. De laatste DMX-waarden blijven op de node staan tot die wegvalt.")
