# -*- coding: utf-8 -*-
"""Persistent row revisions and dependency events; widget readers never fetch data."""
import json

import xbmc
import xbmcgui
from wcs.skin.hero_contract import WIDGET_SLOTS
from wcs.skin import family

MOVIE_ROWS = {'cont_movies', 'my_movies_progress', 'my_movies_added', 'my_movies_unwatched',
              'my_movies_watched', 'my_movies_all', 'recent_played', 'search_my_movies'}
SERIES_ROWS = {'cont_series', 'my_series_next', 'my_series_new', 'my_series_added',
               'my_series_unwatched', 'my_series_upcoming', 'my_series_all', 'recent_played', 'search_my_series'}
NETWORK_ROWS = {'widget_tmdb_popular_movies', 'widget_tmdb_popular_series',
                'widget_kraska_root', 'widget_mycinema_recommended'}
FORYOU_ROWS = {'fy_continue', 'fy_for_you', 'fy_top10', 'fy_because_1', 'fy_because_2', 'fy_because_3',
               'fy_tonight', 'fy_new', 'fy_gems'}                 # Pro tebe (wcs/foryou/widgets.py)
VINYL_ROWS = {'vinyl_' + source for source in ('curator', 'discover', 'top_tracks', 'songs', 'albums', 'artists',
              'playlists', 'genres', 'radios', 'favorite_songs', 'favorite_playlists',
              'favorite_albums', 'favorite_artists')}
ALL_ROWS = (MOVIE_ROWS | SERIES_ROWS | NETWORK_ROWS | VINYL_ROWS | FORYOU_ROWS |
            {'my_movies_genres', 'my_series_genres', 'widget_mytv_channels', 'widget_mytv_next',
             'widget_mycinema_channels', 'widget_game_topics', 'widget_demo_movies', 'widget_demo_series', 'widget_ai_recent', 'widget_csfd_charts'})
SECTIONS = ('foryou', 'mytv', 'mycinema', 'cont', 'game', 'ai', 'kraska', 'tmdb', 'my_movies', 'my_series', 'vinyl')
PREFIX = 'widget.rev.'
service_monitor = None                    # set by the service: publish() may wait for the user there (userfirst.py)


def media_rows(media, membership=False):
    result = set(MOVIE_ROWS if media == 'movie' else SERIES_ROWS) | {'fy_continue'}
    if media != 'movie':
        try:
            from wcs.library2 import shared
            if shared.enabled('tv'):                     # the MyTV rows show the library's progress
                result.update(shared.MYTV_ROWS)
        except Exception:
            pass
    result.update(('widget_game_topics', 'widget_demo_movies' if media == 'movie' else 'widget_demo_series'))
    if membership:
        result.update(('my_movies_genres' if media == 'movie' else 'my_series_genres', 'widget_game_topics'))
    return result


def revisions(rows):
    from wcs.library2 import store
    rows = set(rows) & ALL_ROWS
    if not rows:
        return {}
    conn = store.connect()
    try:
        keys = [PREFIX + row for row in rows]
        found = {r['key'][len(PREFIX):]: int(r['value']) for r in conn.execute(
            'SELECT key,value FROM flags WHERE key IN ({})'.format(','.join('?' for _ in keys)), keys)}
    finally:
        conn.close()
    return {row: found.get(row, 0) for row in rows}


def notify(method, names, force=False):
    # JSON-RPC, not the NotifyAll builtin: the builtin drops every message whose data is not valid JSON, and no
    # JSON survives its parameter parsing
    xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'JSONRPC.NotifyAll', 'params': {
        'sender': 'plugin.video.milionar', 'message': method,
        'data': {'names': sorted(set(names)), 'force': bool(force)}}}))


def changed(rows):
    from wcs.library2 import store
    with store.tx() as conn:
        store.dirty(conn, set(rows) & ALL_ROWS)


def request(rows, force=False):
    if rows:
        notify('skin_widget_refresh', rows, force=force)


def publish(rows=None, warm_sections=True):
    """Service publication: only slots containing changed row types receive a new URL."""
    values = revisions(ALL_ROWS if rows is None else rows)
    home = xbmcgui.Window(10000)
    for row, version in values.items():
        home.setProperty('milionar.rows.' + row, str(version))
    changed_sections = set()
    from wcs.skin import journal, userfirst, warm
    focused = warm.BY_MENU.get(xbmc.getInfoLabel('Container(9000).ListItem.Property(id)'))
    for sec in sorted(SECTIONS, key=lambda value: value != focused):      # the section on screen first
        if warm.hidden(sec):                           # switched off: its rows are not reloaded (warm.py catches up)
            continue
        for n in range(1, WIDGET_SLOTS + 1):
            row = xbmc.getInfoLabel('Skin.String(mil.{}.{}.type)'.format(sec, n))
            if row in values:
                key = 'milionar.widgets.{}.{}'.format(sec, n)
                value = str(values[row])
                if home.getProperty(key) != value:
                    home.setProperty(key, value)
                # The URL of a slot carries this revision from the persistent skin string, so after a restart it is
                # already right when the skin loads: an unchanged row is not fetched a second time.
                if family.active() and xbmc.getInfoLabel('Skin.String(mil.{}.{}.rev)'.format(sec, n)) != value:
                    if journal.installed():            # the service: the row is ready before Kodi asks for it
                        if sec != focused and service_monitor is not None:
                            userfirst.give_way(service_monitor, limit=3.0)     # what the user started goes first
                        params = journal.request_params(sec, n)
                        if params:
                            journal.prepare(dict(params, reload=value))
                    xbmc.executebuiltin('Skin.SetString(mil.{}.{}.rev,{})'.format(sec, n, value))
                    changed_sections.add(sec)
    if rows is not None:
        from wcs.skin import perflog
        perflog.log('published {} changed row types -> sections {}'.format(len(values), sorted(changed_sections) or '-'))
    if changed_sections and warm_sections:
        warm.request(changed_sections)                 # rows of sections that are not on screen reload off-screen


def bootstrap():
    publish(warm_sections=False)                  # the warm thread does the first pass in its own order
