/**
 * ============================================================================
 * ulanziread.c – Liest Tastendrücke vom Ulanzi D200X Stream Deck
 * ============================================================================
 *
 * Dieses Programm öffnet ein HID-Raw-Device (/dev/hidrawN), das parallel
 * zum ulanzi-niri-Daemon geöffnet werden kann (kein exclusiver Zugriff).
 *
 * Das Ulanzi-Protokoll verwendet 1024-Byte-Pakete mit einem 8-Byte-Header:
 *   Bytes 0-1: Magic 0x7c 0x7c
 *   Bytes 2-3: Command (Big-Endian u16)
 *   Bytes 4-7: Payload-Länge (Little-Endian u32)
 *   Bytes 8+:  Payload
 *
 * Relevanter Befehl:
 *   0x0101 (IN_BUTTON) – Signalisiert einen Tastendruck.
 *   Payload (4 Bytes): state | index | marker | value
 *     - marker == 0x01 && value == 1 → gültiger Tastendruck
 *     - index 0-12 → LCD-Buttons (Position 0-12)
 *     - index 15   → Hardware-Button links (Position 14)
 *     - index 16   → Hardware-Button rechts (Position 15, kein Button im Grid)
 *
 * Ausgabe: Die erkannte Position (0-15) als Integer pro Zeile auf stdout.
 * ============================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdint.h>

/* ---------------------------------------------------------
 * Konstanten des HID-Protokolls
 * --------------------------------------------------------- */

/** Größe eines kompletten HID-Pakets in Bytes (vom Ulanzi gesendet) */
#define PACKET_SIZE  1024

/** Größe des Headers (Magic 2B + Cmd 2B + Length 4B) */
#define HEADER_SIZE  8

/** Erstes Magic-Byte zur Identifikation eines gültigen Pakets */
#define MAGIC0       0x7c
/** Zweites Magic-Byte */
#define MAGIC1       0x7c

/** Command-Wert für Input-Button-Events (IN_BUTTON) */
#define CMD_IN_BUTTON 0x0101

/**
 * Einstiegspunkt.
 * Erwartet ein HID-Raw-Device als erstes Argument (z.B. /dev/hidraw2).
 * Läuft in einer Endlosschleife, liest Pakete, parst Tastendrücke
 * und gibt die Positionen auf stdout aus.
 */
int main(int argc, char **argv) {
    const char *dev;

    /* ---- Kommandozeilen-Argument auswerten ---- */
    if (argc >= 2) {
        dev = argv[1];
    } else {
        fprintf(stderr, "Usage: %s /dev/hidrawN\n", argv[0]);
        return 1;
    }

    /* ---- HID-Device öffnen ---- */
    int fd = open(dev, O_RDONLY);
    if (fd < 0) {
        perror("open");
        return 1;
    }

    unsigned char buf[PACKET_SIZE];

    /* ---- Endlose Leseschleife ---- */
    while (1) {
        /* Rohdaten vom HID-Device lesen */
        ssize_t n = read(fd, buf, sizeof(buf));
        if (n < 0) break;              /* Fehler oder Device entfernt */
        if ((size_t)n < HEADER_SIZE) continue; /* zu klein für Header */

        /* ---- Header parsen ---- */

        /* Magic-Bytes prüfen (2 Byte Fixed-Wert 0x7c 0x7c) */
        if (buf[0] != MAGIC0 || buf[1] != MAGIC1) continue;

        /* Command-Wert (Big-Endian u16, Bytes 2-3) */
        uint16_t cmd = ((uint16_t)buf[2] << 8) | buf[3];

        /* Nur IN_BUTTON-Events verarbeiten */
        if (cmd != CMD_IN_BUTTON) continue;

        /* Payload-Länge (Little-Endian u32, Bytes 4-7) */
        uint32_t len = (uint32_t)buf[4]
                     | ((uint32_t)buf[5] << 8)
                     | ((uint32_t)buf[6] << 16)
                     | ((uint32_t)buf[7] << 24);
        if (len < 4) continue;         /* zu kleine Payload */

        /* ---- Button-Payload parsen (4 Bytes) ---- */
        unsigned char state  = buf[HEADER_SIZE + 0];  /* Zustand (0=losgelassen, 1=gedrückt) */
        unsigned char index  = buf[HEADER_SIZE + 1];  /* Hardware-Index des Buttons */
        unsigned char marker = buf[HEADER_SIZE + 2];  /* Marker (0x01 = gültig) */
        unsigned char value  = buf[HEADER_SIZE + 3];  /* Wert (1 = neuer Druck) */

        /* marker=0x01 und value=1 bedeutet: gültiger, neuer Tastendruck */
        if (marker == 0x01 && value == 1) {

            /* Hardware-Index auf unsere Grid-Position mappen:
             *   Index  0-12 → LCD-Buttons 1:1 an Position 0-12
             *   Index 15   → Hardware-Taste links → Position 14
             *   Index 16   → Hardware-Taste rechts → Position 15 (Spezial)
             */
            int pos = -1;
            if (index <= 12) {
                pos = index;
            } else if (index == 15) {
                pos = 14;    /* Hardware-Button links */
            } else if (index == 16) {
                pos = 15;    /* Hardware-Button rechts (z.B. zum Triggern eines Shutdowns) */
            }

            /* Position ausgeben, falls gültig (0-15) */
            if (pos >= 0 && pos <= 15) {
                printf("%d\n", pos);
                fflush(stdout);      /* sofort ausliefern, kein Buffering */
            }
        }
    }

    /* ---- Aufräumen ---- */
    close(fd);
    return 0;
}
