#!/usr/bin/env python3
import os
import signal
import sys
import time

import gi

gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Adw, Gdk, GLib, Gtk

try:
    gi.require_version("Gtk4LayerShell", "1.0")
    from gi.repository import Gtk4LayerShell
except (ImportError, ValueError):
    Gtk4LayerShell = None


BACKLIGHT_PATH = os.environ.get(
    "BRIGHTNESS_OSD_BACKLIGHT", "/sys/class/backlight/intel_backlight"
)
PID_FILE = os.environ.get("BRIGHTNESS_OSD_PID", "/tmp/brightness-osd.pid")
VALUE_FILE = os.environ.get("BRIGHTNESS_OSD_VALUE", "/tmp/brightness-osd.value")
VISIBLE_MS = int(os.environ.get("BRIGHTNESS_OSD_VISIBLE_MS", "1400"))


def read_percent():
    if len(sys.argv) > 1:
        return max(0, min(100, int(round(float(sys.argv[1])))))

    with open(os.path.join(BACKLIGHT_PATH, "brightness"), encoding="utf-8") as handle:
        current = int(handle.read().strip())

    with open(os.path.join(BACKLIGHT_PATH, "max_brightness"), encoding="utf-8") as handle:
        maximum = int(handle.read().strip())

    return max(0, min(100, round(current * 100 / maximum)))


def read_existing_pid():
    try:
        with open(PID_FILE, encoding="utf-8") as handle:
            return int(handle.read().strip())
    except (FileNotFoundError, ValueError):
        return None


def process_exists(pid):
    try:
        os.kill(pid, 0)
        return True
    except ProcessLookupError:
        return False
    except PermissionError:
        return True


def signal_existing(percent):
    pid = None
    for _attempt in range(5):
        pid = read_existing_pid()
        if pid and pid != os.getpid() and process_exists(pid):
            break
        time.sleep(0.03)
    else:
        return False

    with open(VALUE_FILE, "w", encoding="utf-8") as handle:
        handle.write(str(percent))

    try:
        os.kill(pid, signal.SIGUSR1)
        return True
    except ProcessLookupError:
        return False


class BrightnessOsd(Adw.Application):
    def __init__(self, percent):
        super().__init__(application_id="local.brightness.osd")
        self.percent = percent
        self.label = None
        self.bar = None
        self.timeout_id = None

    def update_percent(self, percent):
        self.percent = max(0, min(100, percent))

        if self.label:
            self.label.set_label(f"{self.percent}%")
        if self.bar:
            self.bar.set_value(self.percent)

        if self.timeout_id:
            GLib.source_remove(self.timeout_id)
        self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)

    def handle_refresh(self, *_args):
        try:
            with open(VALUE_FILE, encoding="utf-8") as handle:
                percent = int(handle.read().strip())
        except (FileNotFoundError, ValueError):
            percent = read_percent()

        GLib.idle_add(self.update_percent, percent)

    def do_activate(self):
        with open(PID_FILE, "w", encoding="utf-8") as handle:
            handle.write(str(os.getpid()))

        window = Gtk.ApplicationWindow(application=self)
        window.set_title("Brightness")
        window.set_decorated(False)
        window.set_resizable(False)
        window.set_default_size(190, 58)
        window.set_opacity(0.92)
        window.set_hide_on_close(True)

        if Gtk4LayerShell is not None:
            Gtk4LayerShell.init_for_window(window)
            Gtk4LayerShell.set_layer(window, Gtk4LayerShell.Layer.OVERLAY)
            Gtk4LayerShell.set_anchor(window, Gtk4LayerShell.Edge.TOP, True)
            Gtk4LayerShell.set_margin(window, Gtk4LayerShell.Edge.TOP, 28)
            Gtk4LayerShell.set_keyboard_mode(window, Gtk4LayerShell.KeyboardMode.NONE)

        css = Gtk.CssProvider()
        css.load_from_data(
            b"""
            window {
              background: transparent;
            }
            .osd {
              background: alpha(@window_fg_color, 0.88);
              color: @window_bg_color;
              border-radius: 15px;
              padding: 10px 14px;
              box-shadow: 0 10px 34px alpha(black, 0.34);
            }
            .osd image {
              color: @window_bg_color;
            }
            .percent {
              font-size: 15px;
              font-weight: 700;
            }
            levelbar trough {
              min-height: 6px;
              border-radius: 999px;
              background: alpha(@window_bg_color, 0.24);
            }
            levelbar block.filled {
              min-height: 6px;
              border-radius: 999px;
              background: @window_bg_color;
            }
            """
        )
        Gtk.StyleContext.add_provider_for_display(
            Gdk.Display.get_default(), css, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
        )

        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10)
        box.add_css_class("osd")
        box.set_halign(Gtk.Align.CENTER)
        box.set_valign(Gtk.Align.START)
        box.set_margin_top(28)

        icon = Gtk.Image.new_from_icon_name("display-brightness-symbolic")
        icon.set_pixel_size(22)

        self.label = Gtk.Label(label=f"{self.percent}%")
        self.label.add_css_class("percent")
        self.label.set_width_chars(4)

        self.bar = Gtk.LevelBar.new_for_interval(0, 100)
        self.bar.set_value(self.percent)
        self.bar.set_size_request(96, -1)
        self.bar.set_valign(Gtk.Align.CENTER)

        box.append(icon)
        box.append(self.bar)
        box.append(self.label)
        window.set_child(box)

        signal.signal(signal.SIGUSR1, self.handle_refresh)
        window.present()
        self.timeout_id = GLib.timeout_add(VISIBLE_MS, self.quit)


def main():
    percent = read_percent()
    if signal_existing(percent):
        return 0

    with open(PID_FILE, "w", encoding="utf-8") as handle:
        handle.write(str(os.getpid()))

    app = BrightnessOsd(percent)
    return app.run([])


if __name__ == "__main__":
    raise SystemExit(main())
