#!/usr/bin/env python3
#--------------------------------------------------------------------------------------------------------
# Name: Linux Lite - Lite Update Tray
# Architecture: amd64
# Author: Jerry Bezencon
# Website: https://www.linuxliteos.com
# Language: Python/GTK3 (AyatanaAppIndicator3)
# Licence: GPLv2
#
# Tiny system-tray daemon that surfaces apt update state in the XFCE panel
# via StatusNotifier (Ayatana AppIndicator). Two icon states:
#   - White outline = no updates pending
#   - Green outline = updates available
# Polls `apt list --upgradable` every hour (no root needed; reads the apt
# cache as last refreshed by apt-daily.timer or a manual apt-get update).
# Right-click menu offers an immediate re-poll. Clicking the icon opens
# the lite-updates GUI.
#--------------------------------------------------------------------------------------------------------

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('AyatanaAppIndicator3', '0.1')

from gi.repository import Gtk, AyatanaAppIndicator3, GLib

import os
import signal
import subprocess

APP_ID = "lite-update-tray"
ICON_DIR = "/usr/share/lite-update-tray/icons"
ICON_IDLE = os.path.join(ICON_DIR, "lite-update-tray-idle.svg")
ICON_UPDATES = os.path.join(ICON_DIR, "lite-update-tray-updates.svg")
CHECK_INTERVAL_SECONDS = 3600  # 1 hour


class LiteUpdateTray:
    def __init__(self):
        self.indicator = AyatanaAppIndicator3.Indicator.new(
            APP_ID,
            ICON_IDLE,
            AyatanaAppIndicator3.IndicatorCategory.SYSTEM_SERVICES,
        )
        self.indicator.set_status(AyatanaAppIndicator3.IndicatorStatus.ACTIVE)
        self.indicator.set_title("Linux Lite Updates")
        self.indicator.set_icon_full(ICON_IDLE, "No updates available")

        menu = Gtk.Menu()
        check_item = Gtk.MenuItem(label="Check for Updates Now")
        check_item.connect("activate", self._on_check_now)
        menu.append(check_item)
        menu.show_all()

        self.indicator.set_menu(menu)
        # Clicking the indicator (where the host supports it) triggers
        # the check item — feels natural to most users.
        self.indicator.set_secondary_activate_target(check_item)

        # Schedule first check soon (don't block startup) + recurring.
        GLib.timeout_add_seconds(5, self._first_check)
        GLib.timeout_add_seconds(CHECK_INTERVAL_SECONDS, self._poll_updates)

    def _first_check(self):
        self._poll_updates()
        return False  # one-shot

    def _poll_updates(self):
        """Read apt's already-cached upgradable list. No root needed."""
        try:
            result = subprocess.run(
                ["apt", "list", "--upgradable"],
                capture_output=True, text=True, timeout=30,
            )
            count = 0
            for line in result.stdout.splitlines():
                # apt-list output looks like:
                #   "Listing..."
                #   "pkgname/suite version arch [upgradable from: oldver]"
                if "/" in line and "[upgradable" in line:
                    count += 1

            if count > 0:
                self._set_updates(count)
            else:
                self._set_idle()
        except Exception as e:
            print(f"[lite-update-tray] poll failed: {e}", flush=True)

        return True  # keep the recurring timer alive

    def _on_check_now(self, _item):
        # Immediate re-poll. Doesn't refresh apt cache itself
        # (that needs root) — surfaces whatever apt-daily.timer
        # last fetched, plus any manual apt-get update the user ran.
        self._poll_updates()

    def _set_idle(self):
        self.indicator.set_icon_full(ICON_IDLE, "No updates available")
        self.indicator.set_title("Linux Lite Updates — Up to date")

    def _set_updates(self, count):
        suffix = "" if count == 1 else "s"
        label = f"{count} update{suffix} available"
        self.indicator.set_icon_full(ICON_UPDATES, label)
        self.indicator.set_title(f"Linux Lite Updates — {label}")


def main():
    # Let Ctrl-C / SIGTERM kill the daemon cleanly when run interactively.
    signal.signal(signal.SIGINT, signal.SIG_DFL)
    signal.signal(signal.SIGTERM, signal.SIG_DFL)
    LiteUpdateTray()
    Gtk.main()


if __name__ == "__main__":
    main()
