#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Touch-aktiviertes Backlight mit Vorwarn-Dimmen.
# - Nach DIM_AT Sekunden ohne Touch -> auf DIM_PERCENT % dimmen.
# - Nach IDLE_SECONDS ohne Touch -> Backlight AUS.
# - Bei neuem Touch -> Backlight AN und ursprüngliche Helligkeit wiederherstellen.
#
# Abhängigkeit: python3-evdev
# Kompatibel mit alten evdev-Versionen (set_nonblocking / set_blocking Fallback).

IDLE_SECONDS     = 60   # Nach dieser Inaktivität AUS
DIM_BEFORE_OFF   = 10   # So viele Sekunden vor "AUS" dimmen (0 = kein Dimmen)
DIM_PERCENT      = 30   # Auf wieviel % der Max-Helligkeit dimmen (1..100)

BACKLIGHT_POWER      = "/sys/class/backlight/rpi_backlight/bl_power"      # 0=an, 1=aus
BACKLIGHT_BRIGHTNESS = "/sys/class/backlight/rpi_backlight/brightness"
BACKLIGHT_MAX        = "/sys/class/backlight/rpi_backlight/max_brightness"

import sys, time, signal, errno, select
from evdev import InputDevice, list_devices, ecodes

running = True

def read_int(path, default=None):
    try:
        with open(path) as f:
            return int(f.read().strip())
    except Exception:
        return default

def write_str(path, val):
    try:
        with open(path, "w") as f:
            f.write(val)
        return True
    except Exception as e:
        sys.stderr.write("[touch_backlight] Fehler beim Schreiben {0}: {1}\n".format(path, e))
        return False

def set_backlight(on):
    # on=True -> 0 schreiben; on=False -> 1 schreiben
    write_str(BACKLIGHT_POWER, "0" if on else "1")

def get_backlight_is_on():
    v = read_int(BACKLIGHT_POWER, None)
    if v is None:
        return True
    return v == 0

def get_brightness():
    return read_int(BACKLIGHT_BRIGHTNESS, None)

def set_brightness(val):
    return write_str(BACKLIGHT_BRIGHTNESS, str(int(val)))

def get_max_brightness():
    return read_int(BACKLIGHT_MAX, 255)

def find_touch_devices():
    devices = []
    for path in list_devices():
        try:
            dev = InputDevice(path)
        except Exception:
            continue
        name = (dev.name or "").lower()
        caps = {}
        try:
            caps = dev.capabilities()
        except Exception:
            pass

        has_abs, has_btn_touch = False, False
        try:
            for code in caps.keys():
                if code == ecodes.EV_ABS:
                    has_abs = True
                    break
        except Exception:
            pass
        try:
            if ecodes.EV_KEY in caps:
                keys = caps[ecodes.EV_KEY]
                for k in keys:
                    keycode = k if isinstance(k, int) else k[0]
                    if keycode == ecodes.BTN_TOUCH:
                        has_btn_touch = True
                        break
        except Exception:
            pass

        if ("ft5406" in name) or ("raspberry" in name) or ("touch" in name):
            devices.append(dev)
        elif has_abs or has_btn_touch:
            devices.append(dev)

    uniq = {}
    for d in devices:
        key = getattr(d, 'phys', d.path)
        uniq[key] = d
    return list(uniq.values())

def main():
    global running
    def handle_sigterm(signum, frame):
        global running
        running = False
    signal.signal(signal.SIGTERM, handle_sigterm)
    signal.signal(signal.SIGINT, handle_sigterm)

    touch_devs = find_touch_devices()
    if not touch_devs:
        sys.stderr.write("[touch_backlight] Kein Touch-Input-Gerät gefunden.\n")
        return 1

    # Nonblocking setzen (kompatibel)
    for d in touch_devs:
        try:
            if hasattr(d, "set_nonblocking"):
                d.set_nonblocking(True)
            else:
                d.set_blocking(False)
        except Exception:
            pass

    poller = select.poll()
    for d in touch_devs:
        try:
            poller.register(d.fileno(), select.POLLIN)
        except Exception:
            pass

    # Helligkeit verwalten
    max_b = get_max_brightness() or 255
    base_brightness = get_brightness()
    if base_brightness is None:
        base_brightness = max_b  # Fallback
    dim_brightness = max(1, int(max_b * (DIM_PERCENT / 100.0)))
    dim_enabled = DIM_BEFORE_OFF > 0
    dim_at = max(0, IDLE_SECONDS - DIM_BEFORE_OFF)

    last_touch = time.time()
    is_dimmed = False

    set_backlight(True)  # Start AN
    # Sicherstellen, dass wir mit Basis-Helligkeit starten
    set_brightness(base_brightness)

    while running:
        now = time.time()
        events = []
        try:
            events = poller.poll(500)  # bis 500 ms warten
        except Exception:
            pass

        if events:
            for d in touch_devs:
                while True:
                    try:
                        for ev in d.read():
                            if ev.type == ecodes.EV_KEY or ev.type == ecodes.EV_ABS:
                                last_touch = now
                                # Aufwachen: Backlight an + Helligkeit wiederherstellen
                                if not get_backlight_is_on():
                                    set_backlight(True)
                                    # Kurze Pause, damit das Panel annimmt
                                    time.sleep(0.02)
                                if is_dimmed:
                                    set_brightness(base_brightness)
                                    is_dimmed = False
                        # weiterlesen bis EAGAIN
                    except OSError as e:
                        if e.errno in (errno.EWOULDBLOCK, errno.EAGAIN):
                            break
                        else:
                            break
                    except Exception:
                        break

        idle = now - last_touch

        # Dimmen kurz vor "AUS"
        if dim_enabled and not is_dimmed and idle >= dim_at and idle < IDLE_SECONDS:
            # Basis-Helligkeit nur aktualisieren, wenn wir gerade hell sind
            current = get_brightness()
            if current is not None and current > 0:
                base_brightness = current
            set_brightness(dim_brightness)
            is_dimmed = True

        # Ausschalten nach IDLE_SECONDS
        if idle >= IDLE_SECONDS:
            if get_backlight_is_on():
                set_backlight(False)
            # (dimmed-Flag bleibt; Helligkeit stellen wir beim nächsten Touch zurück)

        time.sleep(0.05)

    return 0

if __name__ == "__main__":
    sys.exit(main())

