# -*- coding: utf-8 -*-
"""The user first: background work of the service steps aside while the user starts something
(docs/plan/skin-optimization, part F).

All Python in Kodi shares one interpreter lock. A plugin run the user started (a dialog, a listing, playing a title)
therefore competes with whatever the service does in the background at that moment: preparing rows, loading sections
off-screen, refreshing caches. Measured on a slow machine, opening a dialog took 0.8 s alone and 2.7 s while rows
were being prepared.

`mark()` is called at the very start of every plugin run that is not a Home row, and for every request the skin sends
to the service. The background loops call `give_way()` between two pieces of work and wait until HOLD seconds have
passed since the last mark. Nothing is cancelled, only delayed, and never longer than `limit`.

Work that is not a loop of the service (the Pro tebe rebuild, the refresh of a show's metadata) calls `breathe()`
between its steps. That does something only in a thread that said `background_thread()` first, so the same code
run by the user (a manual rebuild, a plugin call) never waits for itself.

Kept light: main.py imports this module first thing.
"""
import threading
import time

import xbmcgui

PROPERTY = 'milionar.user.action'            # Home property: time.time() of the last action the user started
OFF = 'milionar.perf.nouserfirst'            # temporary measuring switch: set = background work never waits
HOLD = 2.0                                   # seconds the background stays out of the way after it
_thread = threading.local()


def mark():
    xbmcgui.Window(10000).setProperty(PROPERTY, repr(time.time()))


def active():
    """The user started something less than HOLD seconds ago."""
    home = xbmcgui.Window(10000)
    if home.getProperty(OFF):
        return False
    try:
        return 0 <= time.time() - float(home.getProperty(PROPERTY)) < HOLD
    except ValueError:
        return False


def give_way(monitor=None, limit=6.0):
    """Wait while the user's action is fresh (at most `limit` seconds); True when Kodi is shutting down."""
    waited = 0.0
    while waited < limit and active():
        if monitor is None:
            time.sleep(0.1)
        elif monitor.waitForAbort(0.1):
            return True
        waited += 0.1
    return False


def background_thread():
    """This thread does background work of the service: its breathe() calls step aside for the user."""
    _thread.background = True


def breathe(limit=6.0):
    """Between two steps of a longer job: wait for the user's action in a background thread, nothing elsewhere."""
    if getattr(_thread, 'background', False):
        give_way(None, limit)
