#!/usr/bin/python3

# Copyright (C) 2022 UBports Foundation.
# SPDX-License-Identifier: GPL-3.0-or-later

# This Python script exists because there's no convienient way for usb-moded, a
# system service, to tell a user's systemd to start something. So, the control
# is inverted: this script sits in user systemd & listen for a signal that emits
# from usb-moded, and then start or stop the service as needed.

# This was further modified from /usr/libexec/mtp-server-usb-moded-watcher to
# unlock access to MTP storage devices if/when the device gets unlocked itself.

from gi.repository import GLib, Gio
from subprocess import run

is_greeter_active = True
already_unlocked_storage = False
mtp_enabled = False
old_usb_mode = "undefined"

def handle_unlock():
    global already_unlocked_storage
    print(f"handle_unlock(): mtp_enabled={mtp_enabled}, already_unlocked_storage={already_unlocked_storage}, is_greeter_active={is_greeter_active}")
    if not mtp_enabled or already_unlocked_storage or is_greeter_active:
        # We only care about unlocking locked storage when MTP is enabled and not on lockscreen.
        return

    run(["umtprd", "-cmd:unlock"])
    already_unlocked_storage = True

def handle_current_mode(mode: str):
    global mtp_enabled, old_usb_mode, already_unlocked_storage
    if mode == "busy":
        # This mode is transient, and a signal should be sent when the final
        # mode is reached.
        return

    if mode != old_usb_mode:
        # Act like MTP wasn't enabled as it won't be when switching between
        # "mtp" and "mtp_adb" for example.
        already_unlocked_storage = False
        print(f"handle_current_mode(): {old_usb_mode} -> {mode}")
        old_usb_mode = mode

    mtp_enabled = mode in ("mtp", "mtp_adb") # FIXME: better way for this list?
    handle_unlock()

def handle_dbus_properties_signal(
    obj, sender_name: str, signal_name: str, parameters: GLib.Variant
):
    global is_greeter_active
    if signal_name != "PropertiesChanged":
        return

    is_greeter_active = bool(parameters.get_child_value(1).get_child_value(0).get_child_value(1))
    handle_unlock()

def handle_usb_moded_signal(
    obj, sender_name: str, signal_name: str, parameters: GLib.Variant
):
    if signal_name != "sig_usb_current_state_ind":
        return

    mode = parameters.get_child_value(0).get_string()
    handle_current_mode(mode)

dbus_properties = Gio.DBusProxy.new_for_bus_sync(
    Gio.BusType.SESSION,
    Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
    None, # DBusInterfaceInfo
    "com.lomiri.LomiriGreeter",
    "/com/lomiri/LomiriGreeter",
    "org.freedesktop.DBus.Properties",
    None, # Cancellable
)
is_greeter_active = bool(dbus_properties.call_sync(
    "Get",
    GLib.Variant("(ss)", ("com.lomiri.LomiriGreeter", "IsActive")), # interface_name, property_name
    Gio.DBusCallFlags.NONE,
    500, # TimeoutMsec
    None, # Cancellable
).get_child_value(0))
# TODO: don't track locked state when cable unplugged?
dbus_properties.connect("g-signal", handle_dbus_properties_signal)

usb_moded = Gio.DBusProxy.new_for_bus_sync(
    Gio.BusType.SYSTEM,
    Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
    None, # DBusInterfaceInfo
    "com.meego.usb_moded",
    "/com/meego/usb_moded",
    "com.meego.usb_moded",
    None, # Cancellable
)
usb_moded.connect("g-signal", handle_usb_moded_signal)
current_mode = usb_moded.call_sync(
    "mode_request",
    None, # Parameters
    Gio.DBusCallFlags.NONE,
    500, # TimeoutMsec
    None, # Cancellable
).get_child_value(0).get_string()
handle_current_mode(current_mode)

# TODO: keep track of removable disks via UDisks2 (HintSystem=false) with MountPoints[0]
#       and IdLabel/IdUUID used for e.g.
#       - umtprd '-cmd:addstorage:"/media/phablet/1234-5678" "1234-5678" rw'
#       - umtprd '-cmd:rmstorage:"1234-5678"'

loop = GLib.MainLoop(None)
loop.run()
