#!/usr/bin/env python3
"""Generate resources/skin.milionar/xml/Includes_MilionarSlots.xml (docs/plan/milionar-library/070 §2).

Every Milionář Home section has 7 slots. A slot is up to three Estuary widget includes (poster / episode / categories;
vinyl: square / episode / categories) of which at most one is active: the include conditions read the slot's skin strings
(`mil.<sec>.<n>.type` / `.style`), so the choice made in the slot editor applies at once and the other variants load nothing.
Header, counts and content also come from those strings. The cinematic sections (CINEMA) get their rows from
gen_cinema.py instead. Every section loads
`MilionarContent_<sec>_<n>`: the placeholder playlist until Window(Home).Property(milionar.go.<sec>) is set (wcs/skin/warm.py)
or the section's menu item is focused. Run after changing SECTIONS; never edit the generated file by hand.
"""
import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..'))
from wcs.skin.hero_contract import SECTIONS as CINEMA, WIDGET_SLOTS

ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', '..', 'resources', 'skin.milionar', 'xml')
OUT = os.path.join(ROOT, 'Includes_MilionarSlots.xml')

# section id -> list base (slot n uses list id base + n * 100); the order is the order of the generated includes
SECTIONS = {'mytv': 22000, 'mycinema': 23000, 'cont': 24000, 'game': 25000, 'ai': 26000, 'kraska': 27000, 'tmdb': 28000,
            'my_movies': 29000, 'my_series': 30000, 'vinyl': 31000, 'foryou': 32000}
# Home.xml menu item id of the sections that are gated (the first screen is not)
MENU_IDS = {section: 'mil_continue' if section == 'cont' else 'mil_' + section.replace('_', '') for section in SECTIONS}
SLOTS = WIDGET_SLOTS
MUSIC = 'vinyl'

CONTENT_EMPTY_CONDITION = ('String.IsEmpty(Window(Home).Property(milionar.go.{sec})) + '
                           '!String.IsEqual(Container(9000).ListItem.Property(id),{menu})')
CONTENT = ('plugin://plugin.video.milionar/?action=widget_row&amp;row=$INFO[Skin.String(mil.{sec}.{n}.type)]'
           '&amp;sec={sec}&amp;slot={n}&amp;limit=$INFO[Skin.String(mil.{sec}.{n}.limit)]'
           '&amp;sort=$INFO[Skin.String(mil.{sec}.{n}.sort)]&amp;style=$INFO[Skin.String(mil.{sec}.{n}.style)]'
           '&amp;click=$INFO[Skin.String(mil.{sec}.{n}.click)]&amp;hw=$INFO[Skin.String(mil.{sec}.hidewatched)]'
           '&amp;up=$INFO[Skin.String(mil.{sec}.upcoming)]&amp;reload=$INFO[Skin.String(mil.{sec}.{n}.rev)]'
           '&amp;format=2&amp;profile=$INFO[Window(Home).Property(milionar.low_power)]'
           # the revision of the section's hero buttons: saving them reloads its rows (wcs/skin/slots.touch_hero)
           '&amp;hb=$INFO[Skin.String(mil.{sec}.herorev)]'
           # Pro tebe: the chosen chip (Vše / Filmy / Seriály); empty in the other sections
           '&amp;mode=$INFO[Skin.String(mil.{sec}.mode)]')

STYLE = 'String.IsEqual(Skin.String(mil.{{sec}}.{{n}}.style),{})'
EPISODE = '[{} | {}]'.format(STYLE.format('episode'), STYLE.format('landscape'))
VARIANTS = (('WidgetListPoster', STYLE.format('poster')), ('WidgetListEpisodes', EPISODE),
            ('WidgetListCategories', STYLE.format('categories')))
MUSIC_VARIANTS = (('WidgetListSquare', STYLE.format('square')), ('WidgetListEpisodes', EPISODE),
                  ('WidgetListCategories', STYLE.format('categories')))
ACTIVE = '!String.IsEmpty(Skin.String(mil.{sec}.{n}.type)) + !String.IsEqual(Skin.String(mil.{sec}.{n}.type),off)'
# A builtin of a card or a button goes to the service, which does the addon's quick actions without starting the plugin
# (wcs/skin/commands.py, wcs/skin/local.py). While the service does not listen the skin runs the builtin itself.
ALIVE = '!String.IsEmpty(Window(Home).Property(milionar.service))'
RUN = 'NotifyAll(plugin.video.milionar,run:{})'


def run_lines(command, indent, tag='onclick', condition=''):
    """The two XML lines that run `command`: through the service, or directly when it does not listen."""
    prefix = condition + ' + ' if condition else ''
    return ['{}<{} condition="{}{}">{}</{}>'.format(indent, tag, prefix, ALIVE, RUN.format(command), tag),
            '{}<{} condition="{}!{}">{}</{}>'.format(indent, tag, prefix, ALIVE, command, tag)]


def click_variable(sec, n):
    """What a click on a card of an Estuary widget runs (its include takes one action)."""
    command = '$INFO[Container({}).ListItem.Property(milionar.exec)]'.format(SECTIONS[sec] + n * 100)
    return ['\t<variable name="MilionarClick_{}_{}">'.format(sec, n),
            '\t\t<value condition="{}">{}</value>'.format(ALIVE, RUN.format(command)),
            '\t\t<value>{}</value>'.format(command), '\t</variable>']
# Loading indicator: Kodi's own busy spinner (DialogBusy.xml). A cinematic row shows it in its place (gen_cinema.py ROW),
# a section of Estuary widgets in the middle of its area while none of its rows has a card yet.
SPINNER = '''\t<include name="MilionarSpinner">
\t\t<definition>
\t\t\t<control type="image">
\t\t\t\t<centerleft>50%</centerleft>
\t\t\t\t<centertop>50%</centertop>
\t\t\t\t<width>$PARAM[size]</width>
\t\t\t\t<height>$PARAM[size]</height>
\t\t\t\t<aspectratio>keep</aspectratio>
\t\t\t\t<animation effect="rotate" end="-45" center="auto" time="200" delay="600" loop="true" reversible="false" condition="true">Conditional</animation>
\t\t\t\t<texture colordiffuse="button_focus">spinner.png</texture>
\t\t\t</control>
\t\t\t<control type="image">
\t\t\t\t<centerleft>50%</centerleft>
\t\t\t\t<centertop>50%</centertop>
\t\t\t\t<width>$PARAM[inner]</width>
\t\t\t\t<height>$PARAM[inner]</height>
\t\t\t\t<aspectratio>keep</aspectratio>
\t\t\t\t<animation effect="rotate" end="45" center="auto" time="200" delay="600" loop="true" reversible="false" condition="true">Conditional</animation>
\t\t\t\t<texture flipx="true" colordiffuse="button_focus">spinner.png</texture>
\t\t\t</control>
\t\t</definition>
\t</include>'''                      # written by gen_cinema.py (its rows use it)
SECTION_LOADING = '''\t<include name="MilionarSectionLoading">
\t\t<definition>
\t\t\t<control type="group">
\t\t\t\t<centerleft>50%</centerleft>
\t\t\t\t<centertop>50%</centertop>
\t\t\t\t<width>80</width>
\t\t\t\t<height>80</height>
\t\t\t\t<visible>$PARAM[visible]</visible>
\t\t\t\t<include content="MilionarSpinner">
\t\t\t\t\t<param name="size" value="80"/>
\t\t\t\t\t<param name="inner" value="50"/>
\t\t\t\t</include>
\t\t\t</control>
\t\t</definition>
\t</include>'''


def variants(sec):
    return MUSIC_VARIANTS if sec == MUSIC else VARIANTS


def variable(sec, n):
    return ['\t<variable name="MilionarContent_{}_{}">'.format(sec, n),
            '\t\t<value condition="{}">special://skin/playlists/milionar-empty.xsp</value>'.format(
                CONTENT_EMPTY_CONDITION.format(sec=sec, menu=MENU_IDS[sec])),
            '\t\t<value>{}</value>'.format(CONTENT.format(sec=sec, n=n)),
            '\t</variable>']


def loading(sec):
    """The section is loading and has nothing to show yet."""
    ids = [SECTIONS[sec] + n * 100 for n in range(1, SLOTS + 1)]
    return '\t<expression name="MilionarLoading_{}">[{}] + {}</expression>'.format(
        sec, ' | '.join('Container({}).IsUpdating'.format(i) for i in ids),
        ' + '.join('!Integer.IsGreater(Container({}).NumItems,0)'.format(i) for i in ids))


def widget(sec, n, include, style_condition):
    list_id = SECTIONS[sec] + n * 100
    condition = '{} + {}'.format(ACTIVE, style_condition).format(sec=sec, n=n)
    lines = ['\t\t<include content="{}" condition="{}">'.format(include, condition)]
    lines.append('\t\t\t<param name="content_path" value="$VAR[MilionarContent_{}_{}]"/>'.format(sec, n))
    lines.append('\t\t\t<param name="widget_header" value="$INFO[Skin.String(mil.{}.{}.title)]"/>'.format(sec, n))
    lines.append('\t\t\t<param name="widget_target" value="{}"/>'.format('music' if sec == MUSIC else 'videos'))
    if include == 'WidgetListSquare':
        lines.append('\t\t\t<param name="main_label" value="$INFO[ListItem.Label]"/>')
        lines.append('\t\t\t<param name="sub_label" value="$INFO[ListItem.Label2]"/>')
    lines.append('\t\t\t<param name="onclick_condition" value="!String.IsEmpty(Container({}).ListItem.Property(milionar.exec))"/>'.format(list_id))
    lines.append('\t\t\t<param name="onclick_action" value="$VAR[MilionarClick_{}_{}]"/>'.format(sec, n))
    lines.append('\t\t\t<param name="browse_mode" value="never"/>')
    lines.append('\t\t\t<param name="list_id" value="{}"/>'.format(list_id))
    lines.append('\t\t\t<param name="{}" value="50"/>'.format('item_limit' if include == 'WidgetListCategories' else 'widget_limit'))
    lines.append('\t\t</include>')
    return lines


def build():
    lines = ['<?xml version="1.0" encoding="UTF-8"?>', '<!-- generated by scripts/skin_harness/gen_slots.py: do not edit -->',
             '<includes>']
    for sec in SECTIONS:
        for n in range(1, SLOTS + 1):
            lines.extend(variable(sec, n))
    lines.append(SECTION_LOADING)
    for sec in SECTIONS:
        if sec not in CINEMA:
            for n in range(1, SLOTS + 1):
                lines.extend(click_variable(sec, n))
    lines.extend(loading(sec) for sec in SECTIONS if sec not in CINEMA)
    for sec in SECTIONS:
        if sec in CINEMA:
            continue
        lines.append('\t<include name="MilionarSlots_{}">'.format(sec))
        for n in range(1, SLOTS + 1):
            for include, style_condition in variants(sec):
                lines.extend(widget(sec, n, include, style_condition))
        lines.append('\t</include>')
    lines.append('</includes>')
    return '\n'.join(lines) + '\n'


if __name__ == '__main__':
    with open(OUT, 'w', encoding='utf-8') as handle:
        handle.write(build())
    print('wrote', os.path.relpath(OUT), len(build().splitlines()), 'lines')
