#!/usr/bin/env python3
"""Generate resources/skin.milionar/xml/Includes_MilionarCinema.xml (docs/plan/skin-milionar/190-cinema.md).

A cinematic Home section = a backdrop over the whole window, a hero (title, facts, plot, two buttons, progress) of the
title selected in the rows, and the section's seven widget slots drawn as rounded cards under it. The hero reads the item
of the current row, the list named in Window(Home).Property(milionar.row.<sec>) (a row sets it when it gets focus);
when that row has no title selected (genres, channels) or is empty, it reads the first row that has one. Kodi resolves
a container id when the skin loads, so every hero field is one variable with a value per slot. The texts come from the
items' `milionar.hero.*` properties (wcs/skin/hero.py). Every row is a plugin list (also the rows of the Home
detail, gen_detail.py); PLUGIN / MUSIC say how each kind answers what the cards and the hero ask. Run after changing a
template or gen_slots.CINEMA; never edit the generated file by hand.
"""
import collections
import math
import os

import gen_slots
import gen_music
from wcs.skin.hero_contract import BASES, BUTTON_ANCHOR, BUTTON_GROUP, BUTTON_OFFSETS, BUTTON_PLAIN

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

SLOTS = gen_slots.SLOTS
STYLE = 'String.IsEqual(Skin.String(mil.{{sec}}.{{n}}.style),{})'
POSTER, WIDE, TILE = (('Poster', STYLE.format('poster')),
                      ('Wide', '[{} | {}]'.format(STYLE.format('episode'), STYLE.format('landscape'))),
                      ('Tile', STYLE.format('categories')))
# How an item answers ({i} = the item: ListItem in a card, Container(<id>).ListItem in the hero; {n} = slot):
PLUGIN = {
    'prefix': '', 'styles': (POSTER, WIDE, TILE),
    'resumable': '{i}.IsResumable', 'percent': '{i}.PercentPlayed',
    'series': 'String.IsEqual({i}.Property(milionar.type),tv)',
    # a series has a watched / total badge only when its episodes are counted (a recommendation outside the lists
    # has none: no "0 / 0 ✓")
    'badge': '[String.IsEqual({i}.Property(milionar.type),tv) + !String.IsEmpty({i}.Property(TotalEpisodes))]'
             ' | {i}.IsResumable | Integer.IsGreater({i}.Playcount,0)',
    'poster': '{i}.Art(poster)', 'status': '$VAR[ItemStatusIconVar]', 'watched': '$VAR[WatchedStatusVar]',
    'has_items': 'Integer.IsGreater(Container({lid}).NumItems,0)',
    'shown': 'Integer.IsGreater(Container($PARAM[list_id]).NumItems,0) | Container($PARAM[list_id]).IsUpdating',
    'focusable': 'Integer.IsGreater(Container($PARAM[list_id]).NumItems,0)',
    'content': '<content target="videos" limit="$PARAM[limit]" browse="never">$PARAM[content]</content>',
    'content_param': '$VAR[MilionarContent_{sec}_{n}]',
}
MUSIC = dict(PLUGIN, prefix='Music', styles=(
    ('Square', STYLE.format('square')), ('Record', STYLE.format('record')), ('Artist', STYLE.format('artist')),
    ('MusicWide', WIDE[1]), ('Banner', STYLE.format('banner')), ('Track', STYLE.format('track')),
    ('Genre', STYLE.format('categories'))))
# section id -> (list base, Home menu item id, kind of its items)
SECTIONS = {sec: (gen_slots.SECTIONS[sec], gen_slots.MENU_IDS[sec], MUSIC if sec == 'vinyl' else PLUGIN) for sec in gen_slots.CINEMA}
GAP = 64                                  # row header height = the negative item gap of the rows group list (see ROW)
ROWS_TOP = 496                            # the rows start under the hero; with GAP and the card inset a multiple of 3,
                                          # so a card's progress bar starts on a whole pixel at 1440p too (x 4/3)
ROWS_HEIGHT = 3300                        # seven poster rows: the group list never scrolls (see SECTION_ROWS)
CALM = '!String.IsEqual(Window(Home).Property(milionar.low_power),true)'
MENU = 'String.IsEqual(Container(9000).ListItem.Property(id),{})'

# hero fields: variable suffix -> info label of the selected item ({c} = Container(<list id>).ListItem)
FIELDS = {'Title': '{c}.Property(milionar.hero.title)', 'Kicker': '{c}.Property(milionar.hero.kicker)',
          'Plot': '{c}.Property(milionar.hero.plot)', 'Left': '{c}.Property(milionar.hero.left)',
          'Rating': '{c}.Property(milionar.hero.rating)',
          'Play': '{c}.Property(milionar.hero.play)', 'Detail': '{c}.Property(milionar.hero.detail)',
          'TitleImage': '{c}.Property(milionar.hero.title_image)'}
# the hero buttons of a section (wcs/skin/hero_buttons.py): builtin and label of each
BUTTONS = range(1, len(BUTTON_OFFSETS) + 1)
BUTTON_FIELDS = dict([('B{}'.format(n), '{{c}}.Property(milionar.hero.b{})'.format(n)) for n in BUTTONS]
                     + [('B{}Label'.format(n), '{{c}}.Property(milionar.hero.b{}l)'.format(n)) for n in BUTTONS])
BUTTON_STATES = dict([('HasB{}'.format(n), '!String.IsEmpty({{c}}.Property(milionar.hero.b{}))'.format(n)) for n in BUTTONS]
                     + [('B1Play', 'String.IsEqual({c}.Property(milionar.hero.b1i),play)')])
# the backdrop and its tiny copy (hero.blur, the ambient light of skin.milionar.modern): variable -> item property
BACKDROPS = (('Fanart', 'fanart'), ('Blur', 'blur'))
MUSIC_FIELDS = {'TitleImage': '{c}.Property(milionar.hero.title_image)', 'Meta': '{c}.Property(milionar.hero.meta)'}
MUSIC_STATES = {'HasTitleImage': '!String.IsEmpty({c}.Property(milionar.hero.title_image))',
                'HasPlot': '!String.IsEmpty({c}.Property(milionar.hero.plot))',
                'HasMeta': '!String.IsEmpty({c}.Property(milionar.hero.meta))'}
# hero states: expression suffix -> test of the selected item ("Resumable" comes from the kind of the items)
STATES = {'HasCaption': '!String.IsEmpty({c}.Property(milionar.hero.left))',
          'HasRating': '!String.IsEmpty({c}.Property(milionar.hero.rating))', 'Size_l': 'String.IsEqual({c}.Property(milionar.hero.size),l)',
          'Size_m': 'String.IsEqual({c}.Property(milionar.hero.size),m)',
          'Size_s': 'String.IsEqual({c}.Property(milionar.hero.size),s)',
          'HasTitleImage': '!String.IsEmpty({c}.Property(milionar.hero.title_image))'}
# a MyTV channel (hero.channel): where a title has its rating stands a badge ("on now") with the series it plays
CHANNEL_FIELDS = {'Badge': '{c}.Property(milionar.hero.badge)', 'Now': '{c}.Property(milionar.hero.now)'}
CHANNEL_STATES = {'HasBadge': '!String.IsEmpty({c}.Property(milionar.hero.badge))'}
TITLE_FONTS = (('l', 'font80_title', -4), ('m', 'font_MainMenu', -3), ('s', 'font45_title', -2))
# Optional fixed headlines; all current sections follow the selected item.
STATIC_TITLE = {}


# --------------------------------------------------------------------------- conditions

def slot_numbers():
    return range(1, SLOTS + 1)


def list_id(sec, n):
    return SECTIONS[sec][0] + n * 100


def item(sec, n):
    return 'Container({}).ListItem'.format(list_id(sec, n))


def kind(sec):
    return SECTIONS[sec][2]


def has_items(sec, n):
    return kind(sec)['has_items'].format(lid=list_id(sec, n), n=n)


def ok(sec, n):
    """Slot n has items and a title is selected in it (genres and channels have no hero texts)."""
    return '{} + !String.IsEmpty({}.Property(milionar.hero.title))'.format(has_items(sec, n), item(sec, n))


def chosen(sec, n):
    return 'String.IsEqual(Window(Home).Property(milionar.row.{}),{}) + {}'.format(sec, list_id(sec, n), ok(sec, n))


def source(sec, n):
    """Slot n feeds the hero: it is the current row, or the current row has no title and it is the first that has."""
    any_chosen = ' | '.join('[{}]'.format(chosen(sec, m)) for m in slot_numbers())
    first = ' + '.join(['![{}]'.format(any_chosen), '[{}]'.format(ok(sec, n))]
                       + ['![{}]'.format(ok(sec, m)) for m in range(1, n)])
    return '[{}] | [{}]'.format(chosen(sec, n), first)


def any_of(sec, test):
    """True when the hero's item passes `test` ({c} = its Container(..).ListItem)."""
    return ' | '.join('[[{}] + {}]'.format(source(sec, n), test.format(c=item(sec, n))) for n in slot_numbers())


def above(sec, n):
    """Slot n lies above the current row (a later slot that has items holds the row property)."""
    later = ['[String.IsEqual(Window(Home).Property(milionar.row.{}),{}) + {}]'
             .format(sec, list_id(sec, m), has_items(sec, m)) for m in range(n + 1, SLOTS + 1)]
    return ' | '.join(later) or 'false'


def expression(name, text):
    return '\t<expression name="MilCine{}">{}</expression>'.format(name, text)


def variable(name, values):
    """`values`: (condition or None, text) in the order Kodi tries them."""
    lines = ['\t<variable name="MilCine{}">'.format(name)]
    lines.extend('\t\t<value{}>{}</value>'.format(' condition="{}"'.format(condition) if condition else '', text)
                 for condition, text in values)
    return lines + ['\t</variable>']


def lenders(sec):
    """(k, n): slot k may borrow the hero's title from slot n."""
    return [(k, n) for k in slot_numbers() for n in slot_numbers() if k != n]


def lend(label, k):
    """The property of slot k's layout on a lender's item instead of the item's own."""
    return label.replace('milionar.hero.b', 'milionar.hero.s{}.b'.format(k))


def expressions(sec):
    named = {'Is_{}_{}'.format(sec, n): source(sec, n) for n in slot_numbers()}
    named['Any_' + sec] = ' | '.join('[{}]'.format(ok(sec, n)) for n in slot_numbers())
    states = dict(STATES, Resumable=kind(sec)['resumable'].replace('{i}', '{c}'))
    states.update(MUSIC_STATES if sec == 'vinyl' else CHANNEL_STATES)
    named.update({'{}_{}'.format(name, sec): any_of(sec, test) for name, test in states.items()})
    # A row without titles of its own (genres) borrows the hero's title from another row; while it is the current row
    # its button layout applies, which the lender's items carry under milionar.hero.s<slot>.* (hero_buttons.lent).
    # Use_<k>_<n>: slot k is the current row and the hero shows an item of slot n that carries k's layout.
    named.update({'Use_{}_{}_{}'.format(sec, k, n): 'String.IsEqual(Window(Home).Property(milionar.row.{}),{}) + $EXP[MilCineIs_{}_{}] + '
                                                    '!String.IsEmpty({}.Property(milionar.hero.s{}))'.format(
                                                        sec, list_id(sec, k), sec, n, item(sec, n), k) for k, n in lenders(sec)})
    named['Lent_' + sec] = ' | '.join('$EXP[MilCineUse_{}_{}_{}]'.format(sec, k, n) for k, n in lenders(sec)) or 'false'
    # the button states read the expressions above instead of repeating them (they are long)
    for name, test in BUTTON_STATES.items():
        cases = ['[$EXP[MilCineUse_{}_{}_{}] + {}]'.format(sec, k, n, lend(test, k).format(c=item(sec, n))) for k, n in lenders(sec)]
        cases += ['[$EXP[MilCineIs_{}_{}] + !$EXP[MilCineLent_{}] + {}]'.format(sec, n, sec, test.format(c=item(sec, n)))
                  for n in slot_numbers()]
        named['{}_{}'.format(name, sec)] = ' | '.join(cases)
    named.update({'Above_{}_{}'.format(sec, n): above(sec, n) for n in slot_numbers()})
    return [expression(name, text) for name, text in named.items()]


def variables(sec):
    lines = []
    for name, label in dict(FIELDS, **dict(BUTTON_FIELDS, **(MUSIC_FIELDS if sec == 'vinyl' else CHANNEL_FIELDS))).items():
        lent = [('$EXP[MilCineUse_{}_{}_{}]'.format(sec, k, n), '$INFO[{}]'.format(lend(label, k).format(c=item(sec, n))))
                for k, n in lenders(sec)] if name in BUTTON_FIELDS else []
        lines.extend(variable('{}_{}'.format(name, sec), lent + [
            ('$EXP[MilCineIs_{}_{}]'.format(sec, n), '$INFO[{}]'.format(label.format(c=item(sec, n)))) for n in slot_numbers()]))
    return lines


def shared():
    """What every section uses: the backdrop of the focused section and the card texts."""
    menus = [MENU.format(menu) for _base, menu, _kind in SECTIONS.values()]
    lines = [expression('Active', 'String.IsEqual(Window(Home).Property(milionar.detail.active),true) | ' + ' | '.join(menus))]
    for name, key in BACKDROPS:
        lines.extend(variable(name, [('String.IsEqual(Window(Home).Property(milionar.detail.active),true)',
                                      '$INFO[Window(Home).Property(milionar.detail.{})]'.format(key))] + [
            ('{} + $EXP[MilCineIs_{}_{}]'.format(MENU.format(menu), sec, n),
             '$INFO[{}.Property(milionar.hero.{})]'.format(item(sec, n), key))
            for sec, (_base, menu, _kind) in SECTIONS.items() for n in slot_numbers()]))
    lines.extend(variable('WideArt', [('!String.IsEmpty(ListItem.Art({}))'.format(art), '$INFO[ListItem.Art({})]'.format(art))
                                      for art in ('landscape', 'fanart', 'thumb')] + [(None, '$INFO[ListItem.Icon]')]))
    lines.extend(variable('CardTitle', [('!String.IsEmpty(ListItem.Property(milionar.hero.title))',
                                         '$INFO[ListItem.Property(milionar.hero.title)]'), (None, '$INFO[ListItem.Label]')]))
    return lines


# --------------------------------------------------------------------------- backdrop

BACKDROP = '''\t<include name="MilionarCinemaBackdrop">
\t\t<control type="group">
\t\t\t<depth>DepthBackground</depth>
\t\t\t<visible>$EXP[MilCineActive] + System.AddonIsEnabled(plugin.video.milionar) + !Player.HasVideo</visible>
\t\t\t<animation effect="fade" time="300" condition="{calm}">VisibleChange</animation>
\t\t\t<animation effect="fade" start="0" end="100" time="400" condition="{calm}">WindowOpen</animation>
\t\t\t<animation effect="fade" start="100" end="0" time="300" condition="{calm}">WindowClose</animation>
\t\t\t<control type="image">
\t\t\t\t<include>FullScreenDimensions</include>
\t\t\t\t<texture colordiffuse="cinema_bg">colors/white.png</texture>
\t\t\t</control>
\t\t\t<control type="image">
\t\t\t\t<left>462</left>
\t\t\t\t<top>0</top>
\t\t\t\t<width>1458</width>
\t\t\t\t<height>820</height>
\t\t\t\t<include>MilionarBackdropCollapse</include>
\t\t\t\t<aspectratio>scale</aspectratio>
\t\t\t\t<fadetime>400</fadetime>
\t\t\t\t<texture background="true" diffuse="milionar/cinema/hero_fade.png">$VAR[MilCineFanart]</texture>
\t\t\t\t<visible>!Skin.HasSetting(no_fanart)</visible>
\t\t\t</control>
\t\t</control>
\t</include>'''

# --------------------------------------------------------------------------- progress bar

# One bar for the hero and the cards. The track is the control's own background: Kodi sizes and centres the fill by the
# background texture, so a fill without one is drawn at the texture's own height, half of it above the track. 6 px
# (the texture's height) is a whole number of screen pixels at 720p, 1080p, 1440p and 4K.
PROGRESS = '''\t<include name="MilCineProgress">
\t\t<param name="left">0</param>
\t\t<definition>
\t\t\t<control type="progress">
\t\t\t\t<left>$PARAM[left]</left>
\t\t\t\t<top>$PARAM[top]</top>
\t\t\t\t<width>$PARAM[width]</width>
\t\t\t\t<height>6</height>
\t\t\t\t<texturebg border="3" colordiffuse="33FFFFFF">milionar/cinema/bar.png</texturebg>
\t\t\t\t<lefttexture></lefttexture>
\t\t\t\t<midtexture border="3" colordiffuse="button_focus">milionar/cinema/bar.png</midtexture>
\t\t\t\t<righttexture></righttexture>
\t\t\t\t<overlaytexture></overlaytexture>
\t\t\t\t<info>$PARAM[info]</info>
\t\t\t\t<visible>$PARAM[visible]</visible>
\t\t\t</control>
\t\t</definition>
\t</include>'''

# --------------------------------------------------------------------------- hero

HERO_HEAD = '''\t\t<control type="group">
\t\t\t<left>70</left>
\t\t\t<top>0</top>
\t\t\t<visible>$EXP[MilCineAny_{sec}]</visible>
\t\t\t<control type="label">
\t\t\t\t<top>48</top>
\t\t\t\t<width>1000</width>
\t\t\t\t<height>34</height>
\t\t\t\t<font>font20</font>
\t\t\t\t<textcolor>cinema_dim</textcolor>
\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t<aligny>center</aligny>
\t\t\t\t<label>$VAR[MilCineKicker_{sec}]</label>
\t\t\t</control>'''

HERO_TITLE = '''\t\t\t<control type="label">
\t\t\t\t<left>{left}</left>
\t\t\t\t<top>76</top>
\t\t\t\t<width>1040</width>
\t\t\t\t<height>100</height>
\t\t\t\t<font>{font}</font>
\t\t\t\t<aligny>center</aligny>
\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t<label>{label}</label>
\t\t\t\t<visible>{visible}</visible>
\t\t\t</control>'''

# the title's logo (TMDb) in place of the title text, when the item has one (Pro tebe)
HERO_LOGO = '''\t\t\t<control type="image">
\t\t\t\t<top>70</top>
\t\t\t\t<width>640</width>
\t\t\t\t<height>112</height>
\t\t\t\t<aspectratio align="left" aligny="center">keep</aspectratio>
\t\t\t\t<texture background="true">$VAR[MilCineTitleImage_{sec}]</texture>
\t\t\t\t<visible>$EXP[MilCineHasTitleImage_{sec}]</visible>
\t\t\t</control>'''

HERO_INFO = '''\t\t\t<control type="group">
\t\t\t\t<top>186</top>
\t\t\t\t<visible>$EXP[MilCineHasRating_{sec}] + !$EXP[MilCineHasBadge_{sec}]</visible>
\t\t\t\t<control type="image">
\t\t\t\t\t<width>74</width>
\t\t\t\t\t<height>32</height>
\t\t\t\t\t<texture border="10" colordiffuse="cinema_badge">milionar/cinema/badge.png</texture>
\t\t\t\t</control>
\t\t\t\t<control type="label">
\t\t\t\t\t<width>74</width>
\t\t\t\t\t<height>32</height>
\t\t\t\t\t<font>font_flag</font>
\t\t\t\t\t<textcolor>cinema_ink</textcolor>
\t\t\t\t\t<align>center</align>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<label>TMDb</label>
\t\t\t\t</control>
\t\t\t\t<control type="label">
\t\t\t\t\t<left>90</left>
\t\t\t\t\t<width>200</width>
\t\t\t\t\t<height>32</height>
\t\t\t\t\t<font>font25_title</font>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<label>$VAR[MilCineRating_{sec}]</label>
\t\t\t\t</control>
\t\t\t</control>
\t\t\t<control type="textbox">
\t\t\t\t<top>232</top>
\t\t\t\t<width>600</width>
\t\t\t\t<height>{plot_height}</height>
\t\t\t\t<font>font12</font>
\t\t\t\t<textcolor>cinema_text</textcolor>
\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t<label>$VAR[MilCinePlot_{sec}]</label>
\t\t\t</control>'''

# The line of a channel in the place of the rating: an auto-width badge (a disabled button: the only control that
# draws a texture behind a label of unknown width) and the series the channel plays.
HERO_CHANNEL = '''\t\t\t<control type="grouplist">
\t\t\t\t<top>186</top>
\t\t\t\t<width>1040</width>
\t\t\t\t<height>32</height>
\t\t\t\t<orientation>horizontal</orientation>
\t\t\t\t<itemgap>16</itemgap>
\t\t\t\t<visible>$EXP[MilCineHasBadge_{sec}]</visible>
\t\t\t\t<control type="button">
\t\t\t\t\t<width min="74" max="320">auto</width>
\t\t\t\t\t<height>32</height>
\t\t\t\t\t<font>font_flag</font>
\t\t\t\t\t<label>$VAR[MilCineBadge_{sec}]</label>
\t\t\t\t\t<align>center</align>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<textoffsetx>14</textoffsetx>
\t\t\t\t\t<textcolor>cinema_ink</textcolor>
\t\t\t\t\t<disabledcolor>cinema_ink</disabledcolor>
\t\t\t\t\t<texturefocus border="10" colordiffuse="cinema_badge">milionar/cinema/badge.png</texturefocus>
\t\t\t\t\t<texturenofocus border="10" colordiffuse="cinema_badge">milionar/cinema/badge.png</texturenofocus>
\t\t\t\t\t<enable>false</enable>
\t\t\t\t</control>
\t\t\t\t<control type="label">
\t\t\t\t\t<width min="0" max="700">auto</width>
\t\t\t\t\t<height>32</height>
\t\t\t\t\t<font>font25_title</font>
\t\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<label>$VAR[MilCineNow_{sec}]</label>
\t\t\t\t</control>
\t\t\t</control>'''

# One design for every hero button: 64 px high, PAD on both sides of its content, as wide as its text (Kodi's auto
# width = text + 2 x textoffsetx, within MIN_W..MAX_W). Kodi pads both sides alike, so the room of the play triangle is
# a run of spaces at the start of the label and the icon sits at PAD inside it (25 px fonts: em space 25, en space 12.5,
# space 6.5).
PAD = 36
MIN_W, MAX_W = 120, 520
PLAY_ROOM = '&#x2003;&#x2009;'           # 30 px in the bold font: the triangle's ink (19) and a 13 px gap (measured)
PLAY_X = PAD - 7                         # Estuary's triangle starts 7 px inside its 32 px icon: its ink starts at PAD
ICON_TOP = 388 + 16                      # the 32 px icon centred in the 64 px button

# the buttons of the Home detail (gen_detail.py): a group list like a section's managed buttons below. It needs an id:
# Kodi takes a group list's noop edge (navigation 0) of a group list without one (id 0) for wrapping round.
HERO_FIXED = '''\t\t\t<control type="grouplist" id="{group}">
\t\t\t\t<top>388</top>
\t\t\t\t<width>1340</width>
\t\t\t\t<height>64</height>
\t\t\t\t<orientation>horizontal</orientation>
\t\t\t\t<itemgap>24</itemgap>
\t\t\t\t<onleft>9000</onleft>
\t\t\t\t<onright>noop</onright>
\t\t\t\t<onup>noop</onup>
\t\t\t\t<ondown>{rows}</ondown>
\t\t\t<include content="MilCineHeroButton">
\t\t\t\t<param name="id" value="{play}"/>
\t\t\t\t<param name="label" value="{play_room}$VAR[MilCinePlayLabel_{sec}]"/>
\t\t\t\t<param name="action" value="$VAR[MilCinePlay_{sec}]"/>
\t\t\t\t<param name="rows" value="{rows}"/>
\t\t\t\t<param name="font" value="font25_title"/>
\t\t\t\t<param name="align" value="left"/>
\t\t\t\t<param name="is_detail" value="{is_detail}"/>
\t\t\t</include>
\t\t\t<include content="MilCineHeroButton">
\t\t\t\t<param name="id" value="{detail}"/>
\t\t\t\t<param name="label" value="{detail_label}"/>
\t\t\t\t<param name="action" value="{detail_action}"/>
\t\t\t\t<param name="rows" value="{rows}"/>
\t\t\t\t<param name="is_detail" value="{is_detail}"/>
\t\t\t</include>
{extra_buttons}
\t\t\t</control>
\t\t\t<control type="image">
\t\t\t\t<left>{play_x}</left>
\t\t\t\t<top>{icon_top}</top>
\t\t\t\t<width>32</width>
\t\t\t\t<height>32</height>
\t\t\t\t<texture>icons/now-playing/play.png</texture>
\t\t\t</control>'''

HERO_CAPTION = '''\t\t\t<control type="grouplist">
\t\t\t\t<top>462</top>
\t\t\t\t<width>1040</width>
\t\t\t\t<height>30</height>
\t\t\t\t<orientation>horizontal</orientation>
\t\t\t\t<itemgap>24</itemgap>
\t\t\t\t<control type="group">
\t\t\t\t\t<width>420</width>
\t\t\t\t\t<height>30</height>
\t\t\t\t\t<visible>$EXP[MilCineResumable_{sec}]</visible>'''

HERO_PROGRESS = '''\t\t\t\t\t<include content="MilCineProgress">
\t\t\t\t\t\t<param name="top" value="12"/>
\t\t\t\t\t\t<param name="width" value="420"/>
\t\t\t\t\t\t<param name="info" value="{percent}"/>
\t\t\t\t\t\t<param name="visible" value="$EXP[MilCineIs_{sec}_{n}]"/>
\t\t\t\t\t</include>'''

HERO_TAIL = '''\t\t\t\t</control>
\t\t\t\t<control type="label">
\t\t\t\t\t<width min="0" max="600">auto</width>
\t\t\t\t\t<height>30</height>
\t\t\t\t\t<font>font20</font>
\t\t\t\t\t<textcolor>cinema_dim</textcolor>
\t\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<label>$VAR[MilCineLeft_{sec}]</label>
\t\t\t\t\t<visible>$EXP[MilCineHasCaption_{sec}]</visible>
\t\t\t\t</control>
\t\t\t</control>
\t\t</control>'''

# The buttons of a section's hero: up to four, laid out per widget by wcs/skin/hero_buttons.py and read from the
# item's b<n> / b<n>l properties. A group list: a button without an action is hidden, leaves no gap and the navigation
# skips it. The first button is the primary one; it exists twice, with the play icon (a play action) and plain. Without
# the focus it looks like the others (only its bold label sets it apart): a light button would read as the focused one.
HERO_MANAGED = '''\t\t\t<control type="grouplist" id="{group}">
\t\t\t\t<top>{button_top}</top>
\t\t\t\t<width>1340</width>
\t\t\t\t<height>64</height>
\t\t\t\t<orientation>horizontal</orientation>
\t\t\t\t<itemgap>24</itemgap>
\t\t\t\t<defaultcontrol always="true">{play}</defaultcontrol>
\t\t\t\t<onleft>9000</onleft>
\t\t\t\t<onright>noop</onright>
\t\t\t\t<onup>noop</onup>
\t\t\t\t<ondown>{rows}</ondown>
\t\t\t\t<onback>{rows}</onback>
{items}
\t\t\t</control>
\t\t\t<control type="image">
\t\t\t\t<left>{play_x}</left>
\t\t\t\t<top>{icon_top}</top>
\t\t\t\t<width>32</width>
\t\t\t\t<height>32</height>
\t\t\t\t<texture>icons/now-playing/play.png</texture>
\t\t\t\t<visible>$EXP[MilCineB1Play_{sec}] + $EXP[MilCineHasB1_{sec}]</visible>
\t\t\t</control>'''

HERO_MANAGED_BUTTON = '''\t\t\t\t<control type="button" id="{id}">
\t\t\t\t\t<width min="{min}" max="{max}">auto</width>
\t\t\t\t\t<height>64</height>
\t\t\t\t\t<font>{font}</font>
\t\t\t\t\t<label>{room}$VAR[MilCineB{n}Label_{sec}]</label>
\t\t\t\t\t<align>{align}</align>
\t\t\t\t\t<aligny>center</aligny>
\t\t\t\t\t<textoffsetx>{offset}</textoffsetx>
\t\t\t\t\t<textcolor>{textcolor}</textcolor>
\t\t\t\t\t<focusedcolor>white</focusedcolor>
\t\t\t\t\t<texturefocus border="20" colordiffuse="button_focus">milionar/cinema/button.png</texturefocus>
\t\t\t\t\t<texturenofocus border="20" colordiffuse="{color}">milionar/cinema/{texture}</texturenofocus>
\t\t\t\t\t<onclick condition="!String.IsEmpty(Window(Home).Property(milionar.service))">NotifyAll(plugin.video.milionar,run:$VAR[MilCineB{n}_{sec}])</onclick>
\t\t\t\t\t<onclick condition="!!String.IsEmpty(Window(Home).Property(milionar.service))">$VAR[MilCineB{n}_{sec}]</onclick>
\t\t\t\t\t<visible>{visible}</visible>
\t\t\t\t</control>'''
PRIMARY = dict(font='font25_title', textcolor='white', color='FFFFFFFF', texture='button_ghost.png', offset=PAD,
               min=MIN_W, max=MAX_W, room='')
GHOST = dict(font='font12', align='center', offset=PAD, min=MIN_W, max=MAX_W, textcolor='white', color='FFFFFFFF',
             texture='button_ghost.png', room='')


# Where the hero's context menus open (wcs/skin/menu_anchor.py): Kodi centres a menu on the focused control, and a
# button of a group list has the list's position. This invisible button spans the rows' width (a row: left 46, right 0)
# at the buttons' height; it holds the focus while a menu is open, so the menu opens where a card's menu does, over the
# buttons.
HERO_ANCHOR = '''\t\t<control type="button" id="{id}">
\t\t\t<left>46</left>
\t\t\t<top>388</top>
\t\t\t<right>0</right>
\t\t\t<height>64</height>
\t\t\t<texturefocus />
\t\t\t<texturenofocus />
\t\t\t<onclick>noop</onclick>
\t\t\t<visible allowhiddenfocus="true">false</visible>
\t\t</control>'''

# Pro tebe: what the section shows (Vše / Filmy / Seriály) is a hero button of the manager (wcs/foryou/mode.py). While
# the rows reload after a choice they fade out and a spinner with "Načítám…" stands where the first row's header is, so a
# choice is seen at once.
MODES = ('foryou',)
SWITCHING = 'String.IsEqual(Window(Home).Property(milionar.switching.{sec}),1)'
ROWS_SWITCHING = '''\t\t\t<animation effect="fade" start="100" end="0" time="200" condition="{switching}">Conditional</animation>'''
SWITCHING_SPINNER = '''\t\t<control type="grouplist">
\t\t\t<left>70</left>
\t\t\t<top>{top}</top>
\t\t\t<width>800</width>
\t\t\t<height>46</height>
\t\t\t<orientation>horizontal</orientation>
\t\t\t<itemgap>16</itemgap>
\t\t\t<visible>{switching}</visible>
\t\t\t<animation effect="fade" time="200">VisibleChange</animation>
\t\t\t<control type="group">
\t\t\t\t<width>46</width>
\t\t\t\t<height>46</height>
\t\t\t\t<include content="MilionarSpinner">
\t\t\t\t\t<param name="size" value="46"/>
\t\t\t\t\t<param name="inner" value="32"/>
\t\t\t\t</include>
\t\t\t</control>
\t\t\t<control type="label">
\t\t\t\t<width min="1" max="700">auto</width>
\t\t\t\t<height>46</height>
\t\t\t\t<font>font30_title</font>
\t\t\t\t<aligny>center</aligny>
\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t<label>$ADDON[plugin.video.milionar 35346]</label>
\t\t\t</control>
\t\t</control>'''


def managed_buttons(sec, rows):
    base = BASES[sec]
    play, plain = base + BUTTON_OFFSETS[0], base + BUTTON_PLAIN
    has = '$EXP[MilCineHasB{}_{}]'.format
    icon = '$EXP[MilCineB1Play_{}]'.format(sec)
    buttons = [dict(PRIMARY, id=play, n=1, align='left', room=PLAY_ROOM, visible='{} + {}'.format(has(1, sec), icon)),
               dict(PRIMARY, id=plain, n=1, align='center', visible='{} + !{}'.format(has(1, sec), icon))]
    buttons += [dict(GHOST, id=base + offset, n=n, visible=has(n, sec)) for n, offset in list(zip(BUTTONS, BUTTON_OFFSETS))[1:]]
    items = '\n'.join(HERO_MANAGED_BUTTON.format(sec=sec, **button) for button in buttons)
    return HERO_MANAGED.format(group=base + BUTTON_GROUP, play=play, rows=rows, sec=sec, items=items,
                               button_top=388, icon_top=ICON_TOP, play_x=PLAY_X)


# Clicks: a builtin goes to the service while it listens, else the skin runs it itself (gen_slots.ALIVE / RUN). The
# templates below carry those two lines as text; test_cinema checks them against gen_slots.run_lines.

# --------------------------------------------------------------------------- rows

# A row is three children of the rows group list (usecontrolcoords, itemgap -GAP): the header (size GAP, so it adds
# nothing), the list (drawn GAP lower) and the "more" arrow, which is drawn over the list and gives the GAP back. Each
# needs an id: the group list chains the navigation through them. The list is a panel: only a panel leaves the focused
# layout when the container loses focus.
# The current row sits right under the hero: every row sets Window(Home).Property(milionar.row.<sec>) when it gets
# focus, and the rows above the current one are hidden ($PARAM[hide]), so the rest moves up. A hidden row stays
# focusable (allowhiddenfocus) so that Up reaches it; it then becomes the current row and shows again.
ROW = '''\t<include name="MilCine{prefix}Row{name}">
\t\t<param name="hero_click">false</param>
\t\t<param name="hero_target">0</param>
\t\t<param name="navigation">WidgetListCommon</param>
\t\t<param name="row_visible">true</param>
\t\t<param name="known_items">false</param>
\t\t<param name="limit">50</param>
\t\t<definition>
\t\t\t<control type="label" id="$PARAM[list_id]666">
\t\t\t\t<left>70</left>
\t\t\t\t<top>{label_top}</top>
\t\t\t\t<width>1200</width>
\t\t\t\t<height>46</height>
\t\t\t\t<font>font30_title</font>
\t\t\t\t<aligny>center</aligny>
\t\t\t\t<shadowcolor>cinema_shadow</shadowcolor>
\t\t\t\t<label>$PARAM[widget_header]</label>
\t\t\t\t<visible>{shown}</visible>
\t\t\t\t<visible>!$PARAM[hide]</visible>
\t\t\t</control>
\t\t\t<control type="panel" id="$PARAM[list_id]">
\t\t\t\t<left>46</left>
\t\t\t\t<top>{panel_top}</top>
\t\t\t\t<right>0</right>
\t\t\t\t<height>{panel_height}</height>
\t\t\t\t<visible allowhiddenfocus="{focusable}">{shown}</visible>
\t\t\t\t<visible>!$PARAM[hide]</visible>
\t\t\t\t<onclick condition="$PARAM[hero_click] + !String.IsEmpty(Container($PARAM[list_id]).ListItem.Property(milionar.hero.play))">SetFocus($PARAM[hero_target])</onclick>
{click}
\t\t\t\t<include content="$PARAM[navigation]">
\t\t\t\t\t<param name="list_id" value="$PARAM[list_id]"/>
\t\t\t\t</include>
\t\t\t\t<onfocus>SetProperty(milionar.row.$PARAM[section],$PARAM[list_id],home)</onfocus>
\t\t\t\t<itemlayout width="{slot}" height="{panel_height}">
\t\t\t\t\t<control type="group">
\t\t\t\t\t\t<left>24</left>
\t\t\t\t\t\t<top>{card_top}</top>
{card}
\t\t\t\t\t</control>
\t\t\t\t</itemlayout>
\t\t\t\t<focusedlayout width="{slot}" height="{panel_height}">
\t\t\t\t\t<control type="group">
\t\t\t\t\t\t<left>24</left>
\t\t\t\t\t\t<top>{card_top}</top>
{card}
\t\t\t\t\t\t<control type="image">
\t\t\t\t\t\t\t<left>{frame_at}</left>
\t\t\t\t\t\t\t<top>{frame_at}</top>
\t\t\t\t\t\t\t<width>{frame_w}</width>
\t\t\t\t\t\t\t<height>{frame_h}</height>
\t\t\t\t\t\t\t{frame_texture}
\t\t\t\t\t\t\t<include>Animation_FocusTextureFade</include>
\t\t\t\t\t\t</control>
{lift}\t\t\t\t\t</control>
\t\t\t\t</focusedlayout>
\t\t\t\t{content}
\t\t\t</control>
\t\t\t<control type="group" id="$PARAM[list_id]667">
\t\t\t\t<top>{more_top}</top>
\t\t\t\t<height>{more_height}</height>
\t\t\t\t<visible>{shown}</visible>
\t\t\t\t<visible>!$PARAM[hide]</visible>
\t\t\t\t<control type="group">
\t\t\t\t\t<left>70</left>
\t\t\t\t\t<top>{spinner_top}</top>
\t\t\t\t\t<width>56</width>
\t\t\t\t\t<height>56</height>
\t\t\t\t\t<visible>Container($PARAM[list_id]).IsUpdating + !Integer.IsGreater(Container($PARAM[list_id]).NumItems,0)</visible>
\t\t\t\t\t<include content="MilionarSpinner">
\t\t\t\t\t\t<param name="size" value="56"/>
\t\t\t\t\t\t<param name="inner" value="36"/>
\t\t\t\t\t</include>
\t\t\t\t</control>
\t\t\t\t<control type="group">
\t\t\t\t\t<top>{arrow_group_top}</top>
\t\t\t\t\t<width>76</width>
\t\t\t\t\t<include>MilionarContentRightEdge</include>
\t\t\t\t\t<visible>Container($PARAM[list_id]).HasNext</visible>
\t\t\t\t\t<animation effect="fade" time="200" condition="{calm}">VisibleChange</animation>
\t\t\t\t\t<control type="image">
\t\t\t\t\t\t<width>76</width>
\t\t\t\t\t\t<height>{card_h}</height>
\t\t\t\t\t\t<texture border="20">milionar/cinema/card.png</texture>
\t\t\t\t\t</control>
\t\t\t\t\t<control type="image">
\t\t\t\t\t\t<left>12</left>
\t\t\t\t\t\t<top>{arrow_top}</top>
\t\t\t\t\t\t<width>32</width>
\t\t\t\t\t\t<height>32</height>
\t\t\t\t\t\t<aspectratio>keep</aspectratio>
\t\t\t\t\t\t<texture>overlays/arrowright.png</texture>
\t\t\t\t\t</control>
\t\t\t\t</control>
\t\t\t</control>
\t\t</definition>
\t</include>'''

# The looks the rows are generated for. skin.milionar.modern lifts the focused card towards the viewer; a panel draws
# its items only inside its own area, so its rows get `room` above and below the cards (the panel starts higher and is
# taller, the cards move back by as much and the "more" group starts `room` higher: the group list lays the rows and the
# "more" arrow, level with the cards, out exactly as before) and
# the lifted card's frame and glow are never cut off. Classic: no lift, no room, the file it always had.
# `glow`: px the focus frame of a rounded card reaches beyond Estuary's 16 (its texture keeps the ring as far from the
# card and gives the glow that much more room, gen_modern_textures).
Look = collections.namedtuple('Look', 'name out lift glow')
CLASSIC = Look('classic', OUT, 1.0, 0)
MODERN = Look('modern', os.path.join(ROOT, '..', '..', 'skin.milionar.modern', 'xml', 'Includes_MilionarCinema.xml'), 1.05, 12)
LIFT = '''\t\t\t\t\t\t<animation type="Focus" reversible="false">
\t\t\t\t\t\t\t<effect type="zoom" start="100" end="{zoom}" center="auto" time="260" tween="cubic" easing="out" />
\t\t\t\t\t\t</animation>
\t\t\t\t\t\t<animation type="UnFocus" reversible="false">
\t\t\t\t\t\t\t<effect type="zoom" start="{zoom}" end="100" center="auto" time="180" tween="cubic" easing="out" />
\t\t\t\t\t\t</animation>
'''


def room(look, height):
    """Pixels above and below a card for its lifted frame and glow (a zoomed card grows, its frame too; the frame
    reaches `look.glow` px beyond the 16 px the row always has)."""
    if look.lift <= 1:
        return look.glow
    return int(math.ceil((height + 2 * (16 + look.glow)) * (look.lift - 1) / 2)) + look.glow + 2


T = '\t\t\t\t\t\t'                       # indent of a card inside its layout; the other fields: PLUGIN
# wide card: image, progress, then the title with the note (time left / episode code) on its right
CARD_WIDE = '''{t}<control type="image">
{t}\t<width>326</width>
{t}\t<height>250</height>
{t}\t<texture border="20">milionar/cinema/card.png</texture>
{t}</control>
{t}<control type="image">
{t}\t<width>326</width>
{t}\t<height>183</height>
{t}\t<aspectratio>scale</aspectratio>
{t}\t<texture background="true" fallback="DefaultVideo.png" diffuse="milionar/cinema/mask_wide.png">$VAR[MilCineWideArt]</texture>
{t}</control>
{t}<include content="MilCineProgress">
{t}\t<param name="left" value="16"/>
{t}\t<param name="top" value="195"/>
{t}\t<param name="width" value="294"/>
{t}\t<param name="info" value="{percent}"/>
{t}\t<param name="visible" value="{resumable}"/>
{t}</include>
{t}<control type="label">
{t}\t<left>16</left>
{t}\t<top>205</top>
{t}\t<width>294</width>
{t}\t<height>38</height>
{t}\t<font>font10</font>
{t}\t<aligny>center</aligny>
{t}\t<label>$VAR[MilCineCardTitle]</label>
{t}\t<visible>String.IsEmpty(ListItem.Property(milionar.hero.note))</visible>
{t}</control>
{t}<control type="label">
{t}\t<left>16</left>
{t}\t<top>205</top>
{t}\t<width>150</width>
{t}\t<height>38</height>
{t}\t<font>font10</font>
{t}\t<aligny>center</aligny>
{t}\t<label>$VAR[MilCineCardTitle]</label>
{t}\t<visible>!String.IsEmpty(ListItem.Property(milionar.hero.note))</visible>
{t}</control>
{t}<control type="label">
{t}\t<left>170</left>
{t}\t<top>205</top>
{t}\t<width>140</width>
{t}\t<height>38</height>
{t}\t<font>font20</font>
{t}\t<align>right</align>
{t}\t<aligny>center</aligny>
{t}\t<textcolor>cinema_dim</textcolor>
{t}\t<label>$INFO[ListItem.Property(milionar.hero.note)]</label>
{t}</control>'''

# poster card: the poster (the title while there is none) and, on a dark foot, Estuary's status icon with a series'
# watched / total count
CARD_POSTER = '''{t}<control type="image">
{t}\t<width>232</width>
{t}\t<height>348</height>
{t}\t<texture border="20">milionar/cinema/card.png</texture>
{t}</control>
{t}<control type="textbox">
{t}\t<left>16</left>
{t}\t<top>16</top>
{t}\t<width>200</width>
{t}\t<height>316</height>
{t}\t<font>font12</font>
{t}\t<align>center</align>
{t}\t<aligny>center</aligny>
{t}\t<label>$INFO[ListItem.Label]</label>
{t}\t<visible>String.IsEmpty({poster})</visible>
{t}</control>
{t}<control type="image">
{t}\t<width>232</width>
{t}\t<height>348</height>
{t}\t<aspectratio>scale</aspectratio>
{t}\t<texture background="true" diffuse="milionar/cinema/mask_poster.png">$INFO[{poster}]</texture>
{t}</control>
{t}<control type="image">
{t}\t<width>232</width>
{t}\t<height>348</height>
{t}\t<texture>milionar/cinema/shade_poster.png</texture>
{t}\t<visible>!String.IsEmpty(ListItem.Property(milionar.fy.rank))</visible>
{t}</control>
{t}<control type="label">
{t}\t<left>14</left>
{t}\t<top>236</top>
{t}\t<width>200</width>
{t}\t<height>110</height>
{t}\t<font>font80_title</font>
{t}\t<aligny>bottom</aligny>
{t}\t<textcolor>white</textcolor>
{t}\t<shadowcolor>FF000000</shadowcolor>
{t}\t<label>$INFO[ListItem.Property(milionar.fy.rank)]</label>
{t}\t<visible>!String.IsEmpty(ListItem.Property(milionar.fy.rank))</visible>
{t}</control>
{t}<control type="group">
{t}\t<visible>{badge}</visible>
{t}\t<control type="image">
{t}\t\t<width>232</width>
{t}\t\t<height>348</height>
{t}\t\t<texture>milionar/cinema/shade_poster.png</texture>
{t}\t</control>
{t}\t<control type="label">
{t}\t\t<left>16</left>
{t}\t\t<top>306</top>
{t}\t\t<width>166</width>
{t}\t\t<height>32</height>
{t}\t\t<font>font20_title</font>
{t}\t\t<align>right</align>
{t}\t\t<aligny>center</aligny>
{t}\t\t<label>{watched}</label>
{t}\t\t<visible>{series}</visible>
{t}\t</control>
{t}\t<control type="image">
{t}\t\t<left>190</left>
{t}\t\t<top>306</top>
{t}\t\t<width>32</width>
{t}\t\t<height>32</height>
{t}\t\t<texture>{status}</texture>
{t}\t</control>
{t}</control>'''

CARD_TILE = '''{t}<control type="image">
{t}\t<width>316</width>
{t}\t<height>96</height>
{t}\t<texture border="20">milionar/cinema/card.png</texture>
{t}</control>
{t}<control type="image">
{t}\t<left>18</left>
{t}\t<top>22</top>
{t}\t<width>52</width>
{t}\t<height>52</height>
{t}\t<aspectratio>keep</aspectratio>
{t}\t<texture fallback="DefaultFolder.png">$INFO[ListItem.Icon]</texture>
{t}</control>
{t}<control type="label">
{t}\t<left>84</left>
{t}\t<top>0</top>
{t}\t<width>220</width>
{t}\t<height>96</height>
{t}\t<font>font10</font>
{t}\t<aligny>center</aligny>
{t}\t<label>$INFO[ListItem.Label]</label>
{t}</control>'''

# row style -> (card template, card width, card height)
ROWS = {'Wide': (CARD_WIDE, 326, 250), 'Poster': (CARD_POSTER, 232, 348), 'Tile': (CARD_TILE, 316, 96)}
ROWS.update(gen_music.ROWS)
ACTIVE = '!String.IsEmpty(Skin.String(mil.{sec}.{n}.type)) + !String.IsEqual(Skin.String(mil.{sec}.{n}.type),off)'

# The rows: a group list under the hero, tall enough for every row, so it never scrolls: the current row is always its
# first visible one. A new current row eases in (one animation per slot, started by the row property).
SECTION_ROWS = '''\t\t<control type="grouplist" id="{rows}">
\t\t\t<left>0</left>
\t\t\t<top>{top}</top>
\t\t\t<right>0</right>
\t\t\t<height>{height}</height>
\t\t\t<orientation>vertical</orientation>
\t\t\t<itemgap>-{gap}</itemgap>
\t\t\t<onup>{play}</onup>
\t\t\t<ondown>20001</ondown>
\t\t\t<usecontrolcoords>true</usecontrolcoords>
\t\t\t<pagecontrol>{page}</pagecontrol>'''
ROW_EASE = '''\t\t\t<animation type="Conditional" condition="String.IsEqual(Window(Home).Property(milionar.row.{sec}),{lid}) + {calm}" reversible="false">
\t\t\t\t<effect type="slide" start="0,36" end="0,0" time="260" tween="cubic" easing="out" />
\t\t\t\t<effect type="fade" start="30" end="100" time="220" />
\t\t\t</animation>'''
SLOT = '''\t\t\t<include content="{include}" condition="{condition}">
\t\t\t\t<param name="content" value="{content}"/>
\t\t\t\t<param name="widget_header" value="$INFO[Skin.String(mil.{sec}.{n}.title)]"/>
\t\t\t\t<param name="section" value="{sec}"/>
\t\t\t\t<param name="hide" value="$EXP[MilCineAbove_{sec}_{n}]"/>
\t\t\t\t<param name="list_id" value="{lid}"/>
\t\t\t\t<param name="hero_click" value="String.IsEqual(Skin.String(mil.{sec}.{n}.click),hero)"/>
\t\t\t\t<param name="hero_target" value="{play}"/>
\t\t\t</include>'''


def row(items, name, look=CLASSIC):
    """The row include of one style for one kind of items."""
    card, width, height = ROWS[name]
    facts = {key: items[key].format(i='ListItem') for key in ('resumable', 'percent', 'series', 'badge', 'poster')}
    # the focus frame follows the card's shape: a rounded rectangle, or the ring of a round music card
    pad = 16 + look.glow
    if name in gen_music.ROUND:                                 # a ring around the circle (a stretched texture)
        frame_w = frame_h = width + 2 * pad
        frame_texture = gen_music.CIRCLE_FRAME
    else:
        frame_w, frame_h = width + 2 * pad, height + 2 * pad
        frame_texture = '<texture border="{}" colordiffuse="button_focus">milionar/cinema/focus.png</texture>'.format(24 + pad)
    more_h = width if name in gen_music.ROUND else height      # the "more" panel is as tall as the card's shape
    list_height = -(-(height + 32) // 3) * 3                    # 16 px around the card for the focus frame; see ROWS_TOP
    extra = room(look._replace(glow=pad - 16), frame_h - 2 * pad)
    return ROW.format(prefix=items['prefix'], name=name, gap=GAP, label_top=GAP - 46, shown='[{} | $PARAM[known_items]] + $PARAM[row_visible]'.format(items['shown']),
                      focusable='[{} | $PARAM[known_items]] + $PARAM[row_visible]'.format(items['focusable']), content=items['content'], slot=width + 24,
                      panel_top=GAP - extra, panel_height=list_height + 2 * extra, card_top=16 + extra,
                      lift=LIFT.format(zoom=int(round(look.lift * 100))) if extra else '', arrow_group_top=16,
                      card=card.format(t=T, status=items['status'], watched=items['watched'], **facts),
                      frame_at=-pad, frame_w=frame_w, frame_h=frame_h, frame_texture=frame_texture, more_top=GAP - list_height - extra,
                      more_height=GAP + list_height, card_h=more_h, arrow_top=(more_h - 32) // 2,
                      spinner_top=16 + (more_h - 56) // 2, calm=CALM,
                      click='\n'.join(gen_slots.run_lines(
                          '$INFO[Container($PARAM[list_id]).ListItem.Property(milionar.exec)]', '\t\t\t\t', condition='!$PARAM[hero_click] + '
                          '!String.IsEmpty(Container($PARAM[list_id]).ListItem.Property(milionar.exec))')))



HERO_BUTTON = '''\t<include name="MilCineHeroButton">
\t\t<param name="font">font12</param>
\t\t<param name="align">center</param>
\t\t<param name="textcolor">white</param>
\t\t<param name="texture">milionar/cinema/button_ghost.png</param>
\t\t<param name="color">FFFFFFFF</param>
\t\t<param name="is_detail">false</param>
\t\t<param name="visible">true</param>
\t\t<definition>
\t\t\t<control type="button" id="$PARAM[id]">
\t\t\t\t<width min="{min}" max="{max}">auto</width>
\t\t\t\t<height>64</height>
\t\t\t\t<font>$PARAM[font]</font>
\t\t\t\t<label>$PARAM[label]</label>
\t\t\t\t<align>$PARAM[align]</align>
\t\t\t\t<aligny>center</aligny>
\t\t\t\t<textoffsetx>{pad}</textoffsetx>
\t\t\t\t<textcolor>$PARAM[textcolor]</textcolor>
\t\t\t\t<focusedcolor>white</focusedcolor>
\t\t\t\t<texturefocus border="20" colordiffuse="button_focus">milionar/cinema/button.png</texturefocus>
\t\t\t\t<texturenofocus border="20" colordiffuse="$PARAM[color]">$PARAM[texture]</texturenofocus>
\t\t\t\t<onclick condition="!String.IsEmpty(Window(Home).Property(milionar.service))">NotifyAll(plugin.video.milionar,run:$PARAM[action])</onclick>
\t\t\t\t<onclick condition="!!String.IsEmpty(Window(Home).Property(milionar.service))">$PARAM[action]</onclick>
\t\t\t\t<onup>noop</onup>
\t\t\t\t<ondown>$PARAM[rows]</ondown>
\t\t\t\t<onback condition="$PARAM[is_detail] + !String.IsEmpty(Window(Home).Property(milionar.service))">NotifyAll(plugin.video.milionar,run:RunPlugin(plugin://plugin.video.milionar/?action=skin_detail_back))</onback>
\t\t\t\t<onback condition="$PARAM[is_detail] + !!String.IsEmpty(Window(Home).Property(milionar.service))">RunPlugin(plugin://plugin.video.milionar/?action=skin_detail_back)</onback>
\t\t\t\t<visible>$PARAM[visible]</visible>
\t\t\t</control>
\t\t</definition>
\t</include>'''


def hero_controls(ids, progress=(), buttons=None, channel=False, static_title=None):
    """`buttons`: the section's managed buttons (managed_buttons); None: the two fixed ones of the Home detail.
    `channel`: the section's rows may hold MyTV channels (the badge line); `static_title`: (text, font step) of a
    headline that does not follow the selected item."""
    ids = dict({'is_detail': 'false', 'detail_label': '$LOCALIZE[31991]',
                'detail_action': '$VAR[MilCineDetail_{}]'.format(ids['sec']),
                'extra_buttons': '', 'play_room': PLAY_ROOM, 'play_x': PLAY_X, 'icon_top': ICON_TOP,
                'plot_height': 140}, **ids)
    lines = [HERO_HEAD.format(**ids)]
    if static_title:
        text, step = static_title
        font, left = next((f, l) for st, f, l in TITLE_FONTS if st == step)
        lines.append(HERO_TITLE.format(font=font, left=left, label=text, visible='true', **ids))
    else:
        lines.extend(HERO_TITLE.format(font=font, left=left, label='$VAR[MilCineTitle_{}]'.format(ids['sec']),
                                       visible='$EXP[MilCineSize_{}_{}] + !$EXP[MilCineHasTitleImage_{}]'.format(
                                           step, ids['sec'], ids['sec']), **ids)
                     for step, font, left in TITLE_FONTS)
        lines.append(HERO_LOGO.format(**ids))
    lines.append(HERO_INFO.format(**ids))
    if channel:
        lines.append(HERO_CHANNEL.format(**ids))
    lines.append(HERO_FIXED.format(**ids) if buttons is None else buttons)
    lines.append(HERO_CAPTION.format(**ids))
    lines.extend(HERO_PROGRESS.format(percent=percent, n=n, **ids) for percent, n in progress)
    lines.append(HERO_TAIL.format(**ids))
    return lines


def section(sec):
    """The section's content for its Home group: the hero and the slots as cinema rows (the header and a plugin
    row's content are the ones of gen_slots.py)."""
    base = SECTIONS[sec][0]
    # `play`: where Up from the rows and the click option "focus hero buttons" go: the buttons' group list
    ids = {'sec': sec, 'play': base + BUTTON_GROUP, 'detail': base + BUTTON_OFFSETS[1], 'rows': base + 1, 'page': base + 9,
           'calm': CALM}
    buttons = managed_buttons(sec, ids['rows'])
    lines = ['\t<include name="MilionarCinema_{}">'.format(sec)]
    lines.extend(gen_music.hero_controls(TITLE_FONTS, ids, buttons) if sec == 'vinyl' else
                 hero_controls(ids, [(kind(sec)['percent'].format(i=item(sec, n)), n) for n in slot_numbers()], buttons,
                               channel=True, static_title=STATIC_TITLE.get(sec)))
    lines.append(SECTION_ROWS.format(top=ROWS_TOP, height=ROWS_HEIGHT, gap=GAP, **ids))
    if sec in MODES:
        lines.append(ROWS_SWITCHING.format(switching=SWITCHING.format(sec=sec)))
    lines.extend(ROW_EASE.format(lid=list_id(sec, n), **ids) for n in slot_numbers())
    items = kind(sec)
    for n in slot_numbers():
        for name, style in items['styles']:
            lines.append(SLOT.format(include='MilCine{}Row{}'.format(items['prefix'], name),
                                     condition='{} + {}'.format(ACTIVE, style).format(sec=sec, n=n),
                                     content=items['content_param'].format(sec=sec, n=n), sec=sec, n=n,
                                     lid=list_id(sec, n), play=ids['play']))
    lines.append('\t\t</control>')
    lines.append(HERO_ANCHOR.format(id=base + BUTTON_ANCHOR))
    if sec in MODES:
        lines.append(SWITCHING_SPINNER.format(top=ROWS_TOP + GAP - 46, switching=SWITCHING.format(sec=sec)))
    return lines + ['\t</include>']


def build(look=CLASSIC):
    lines = ['<?xml version="1.0" encoding="UTF-8"?>', '<!-- generated by scripts/skin_harness/gen_cinema.py: do not edit -->',
             '<includes>']
    for sec in SECTIONS:
        lines.extend(expressions(sec))
    lines.extend(shared())
    for sec in SECTIONS:
        lines.extend(variables(sec))
    lines.extend(row(items, name, look) for items in (PLUGIN, MUSIC) for name, _style in items['styles'])
    lines.append(gen_slots.SPINNER)
    lines.append(HERO_BUTTON.format(min=MIN_W, max=MAX_W, pad=PAD))
    lines.append(PROGRESS)
    lines.append(BACKDROP.format(calm=CALM))
    for sec in SECTIONS:
        lines.extend(section(sec))
    lines.append('</includes>')
    return '\n'.join(lines) + '\n'


if __name__ == '__main__':
    for look in (CLASSIC, MODERN):
        text = build(look)
        with open(look.out, 'w', encoding='utf-8') as handle:
            handle.write(text)
        print('wrote', os.path.relpath(look.out), len(text.splitlines()), 'lines')
