"""
There is no wxWidgets way to tell all platforms to prevent idle-sleep.
"""

import sys


if sys.platform.startswith("win"):
    # https://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx
    import ctypes

    ES_CONTINUOUS = 0x80000000
    ES_SYSTEM_REQUIRED = 0x00000001

    def set_keep_awake(b: bool) -> bool:
        """
        Returns True if the operation was successful.
        """
        v = ES_CONTINUOUS
        if b:
            v |= ES_SYSTEM_REQUIRED
        return ctypes.windll.kernel32.SetThreadExecutionState(v) != 0


elif sys.platform.startswith("linux"):
    # On Linux pretty much everyone uses D-Bus for requesting inhibition on power management. Unfortunately
    # this means there are specific implementations for every desktop-environment.
    # For debugging or developing new interfaces:
    #   gdbus introspect --session --dest org.freedesktop.PowerManagement
    #       --object-path /org/freedesktop/PowerManagement
    #   dbus-send --session --print-reply --type=method_call --reply-timeout=2000
    #       --dest=org.freedesktop.PowerManagement /org/freedesktop/PowerManagement/Inhibit
    #       org.freedesktop.PowerManagement.Inhibit.HasInhibit
    #   dbus-send --session --print-reply --type=method_call --reply-timeout=2000
    #       --dest=org.freedesktop.PowerManagement /org/freedesktop/PowerManagement/Inhibit
    #       org.freedesktop.PowerManagement.Inhibit.Inhibit string:'cbtest' string:'yes'
    #   dbus-send --session --print-reply --type=method_call --reply-timeout=2000
    #       --dest=org.freedesktop.PowerManagement /org/freedesktop/PowerManagement/Inhibit
    #       org.freedesktop.PowerManagement.Inhibit.UnInhibit uint32:26
    import dbus
    from dbus.mainloop.glib import DBusGMainLoop

    # wxWidgets uses GTK (GLib) on Linux
    DBusGMainLoop(set_as_default=True)

    INHIBIT_COOKIE = None

    def set_keep_awake(b: bool) -> bool:
        """
        Return if the requested state was reached by all available inhibitors.
        """
        global INHIBIT_COOKIE
        try:
            power_management = dbus.SessionBus().get_object(
                "org.freedesktop.PowerManagement", "/org/freedesktop/PowerManagement/Inhibit"
            )
            if b:
                if not INHIBIT_COOKIE:
                    INHIBIT_COOKIE = power_management.Inhibit("clarabot-nano", "serving-remote-requests")
            elif INHIBIT_COOKIE:
                power_management.UnInhibit(INHIBIT_COOKIE)
                INHIBIT_COOKIE = None
        except dbus.DBusException:
            INHIBIT_COOKIE = None  # assume that we loose the cookie
            return False
        return bool(INHIBIT_COOKIE) is b


elif sys.platform.startswith("darwin"):

    import objc

    _IOKIT_GLOBALS = dict()
    _IOKIT_LIB = objc.loadBundle("IOKit", _IOKIT_GLOBALS, bundle_identifier="com.apple.framework.IOKit")
    objc.loadBundleFunctions(
        _IOKIT_LIB,
        _IOKIT_GLOBALS,
        [
            (
                "IOPMAssertionCreateWithName",
                b"".join(
                    [
                        objc._C_INT,  # return value; 0 indicates success
                        objc._C_ID,  # String object (auto-cast); one of the assertion type constants
                        objc._C_UINT,  # unsigned integer; one of the assertion level constants
                        objc._C_ID,  # String object (auto-cast); short description (max 127) of the reason
                        b"".join(  # unsigned integer; identifier of the new assertion, used for releasing it later
                            [
                                objc._C_OUT,  # capture reference as output; becomes an additional return value
                                objc._C_PTR,  # pointer
                                objc._C_UINT,  # unsigned integer
                            ]
                        ),
                    ]
                ),
            ),
            (
                "IOPMAssertionRelease",
                b"".join(
                    [
                        objc._C_INT,  # return value; 0 indicates success
                        objc._C_UINT,  # unsigned integer; assertion id from the create-fn
                    ]
                ),
            ),
        ],
    )

    INHIBIT_ASSERTION_ID = None

    def set_keep_awake(b: bool) -> bool:
        """
        Returns True if the operation was successful.
        """
        global INHIBIT_ASSERTION_ID
        if b:
            if INHIBIT_ASSERTION_ID is None:
                # NOTE: Assertion types are poorly described in the docs, these are relevant to us:
                #   - "PreventUserIdleSystemSleep" - prevents automatic sleep due to user inactivity
                #   - "NoIdleSleep" - system will not idle sleep, but may sleep for other reasons
                #   - "PreventSystemSleep" - prevents idle sleep, allows "Dark Wake" for any length of time
                error_code, assertion_id = _IOKIT_GLOBALS["IOPMAssertionCreateWithName"](
                    "PreventUserIdleSystemSleep", 255, "clarabot-nano serving-remote-requests", None
                )
                if error_code == 0:
                    INHIBIT_ASSERTION_ID = assertion_id
                else:
                    return False
        elif INHIBIT_ASSERTION_ID is not None:
            error_code = _IOKIT_GLOBALS["IOPMAssertionRelease"](INHIBIT_ASSERTION_ID)
            INHIBIT_ASSERTION_ID = None  # assume that we've lost the assertion prior for some reason
            if error_code != 0:
                return False
        return True


else:
    # NOTE: BSD systems also use DBus (because the DEs use it), so they could be supported easily... maybe?
    raise NotImplementedError()
