# wcs/games/DialogMilionarGame.py
"""Hra Milionář jako modální karta (klasická, nebo moderní podle nastavení) pro všechna místa mimo MyTV.

Drop-in náhrada za TextGameWindow: stejné argumenty konstruktoru, ale otevře se přímo Milionář v modální kartě.
Vzhled (XML) vybírá nastavení doplňku `milionar_game_design` (wcs/games/designs.py); hru řídí MillionaireMyTVAdapter.
"""
import importlib
import threading
import time

import xbmc
import xbmcaddon
import xbmcgui

from wcs.games import designs, game_settings
from wcs.games.millionaire.subject import Subject

ACTION_MOVE_LEFT = 1
ACTION_MOVE_RIGHT = 2
ACTION_PREVIOUS_MENU = 10
ACTION_NAV_BACK = 92
HOME_MENU_ID = 9000                  # the main menu list of the Home window
EXIT_FOCUS = 9691                    # "stay in the game" of the exit confirmation
EXIT_PROPERTY = 'WCS.Game.Exit'
SETTINGS_PROPERTY = 'WCS.Game.Settings'  # the settings of the game are open over the title card
SETTINGS_BUTTON = 9604                   # the title card's button that opens them


def _game_probe():
    """The game probe of the dev harness (wcs/devtools is left out of release ZIPs): the module, or None."""
    try:
        return importlib.import_module('wcs.devtools.game_probe')
    except ImportError:
        return None


class MilionarGameModal(xbmcgui.WindowXMLDialog):
    def __new__(cls, *args, **kwargs):
        # Kodi reads the window XML from the constructor arguments of __new__: ignore what the caller passes
        # (callers still pass the old fullscreen game XML) and load the XML of the design chosen in the settings.
        design = kwargs.get('design') or designs.current()
        window = super().__new__(cls, design.xml, xbmcaddon.Addon().getAddonInfo('path'), 'Default', 'Default')
        window._design = design
        return window

    def __init__(self, xmlFilename=None, addonPath=None, skin='Default', theme='Default', title='', fanart='',
                 plot='', genre='', media_type='movie', actor_data=None, banner_fanart=None, episode_title='',
                 season_number=None, episode_number=None, content=None, design=None, year='', subject=None,
                 demo_id='', mode=None, story_source=None):
        # xmlFilename / addonPath / skin jsou tu jen kvůli kompatibilitě s TextGameWindow.
        super().__init__(self._design.xml, xbmcaddon.Addon().getAddonInfo('path'), 'Default', 'Default')
        self.title = title
        self.poster = fanart or ''
        self.backdrop = banner_fanart or ''
        self.plot = plot
        self.genre = genre
        self.year = year
        self.media_type = 'series' if media_type == 'tv' else (media_type or 'movie')
        self.actor_data = actor_data or {}
        self.episode_title = episode_title
        self.season_number = season_number
        self.episode_number = episode_number
        self.content = content
        self.subject = subject or Subject(title=title, media_type=self.media_type, plot=plot, genre=genre,
                                          year=year, poster=self.poster, backdrop=self.backdrop,
                                          episode_title=episode_title, season_number=season_number,
                                          episode_number=episode_number, person=actor_data)
        self.demo_id = demo_id or (getattr(getattr(content, 'title', None), 'id', '')
                                   if getattr(content, 'name', '') == 'demo' else '')
        self.story_source = story_source
        from wcs.games.story.content import demo_available
        self.story_available = self._design.modern and self.media_type in ('movie', 'series', 'episode') and (
            content is None or demo_available(self.demo_id) or story_source is not None)
        wanted = mode or xbmcaddon.Addon().getSetting('game_last_mode') or 'millionaire'
        self.mode = 'story' if wanted == 'story' and self.story_available else 'millionaire'
        self._adapter = None
        self._closing = False
        self._starting = False
        self._focus_serial = 0

    def doModal(self):
        try:
            super().doModal()
        finally:                                      # the window may also be closed by Kodi itself (Dialog.Close)
            self._dispose_adapter()
            probe = _game_probe()
            if probe:
                probe.detach(self)

    def onInit(self):
        if self._adapter is not None:
            return
        self.setProperty('WCS.Game.Mode', self.mode)
        self.setProperty('WCS.Game.StoryAvailable', 'true' if self.story_available else '')
        for key, value in {'Name': self.title, 'Year': str(self.year or ''), 'Genre': self.genre,
                           'Poster': self.poster, 'Fanart': self.backdrop or self.poster}.items():
            self.setProperty('WCS.Game.' + key, value)
        if self.mode == 'story':
            from wcs.games.story.adapter import StoryAdapter
            self._adapter = StoryAdapter(self, self.subject, self.demo_id, self.story_source)
        else:
            from wcs.games.MillionaireMyTVAdapter import MillionaireMyTVAdapter
            self._adapter = MillionaireMyTVAdapter(
                dialog=self, series_id='', title=self.title, plot=self.plot, poster=self.poster,
                fanart=self.backdrop or self.poster, logo='', episode_title=self.episode_title,
                season_number=self.season_number, episode_number=self.episode_number, media_type=self.media_type,
                genre=self.genre, actor_data=self.actor_data, content=self.content, design=self._design, year=self.year)
        self._starting = True
        try:
            self._adapter.start()
        finally:
            self._starting = False
        probe = _game_probe()                         # dev harness only: no-op unless harness/ENABLED exists
        if probe:
            probe.attach_if_enabled(self)

    # ---- API, které volá MillionaireMyTVAdapter (stejné jako MyTV dialog) ----

    def setFocusId(self, control_id):
        # While the window is still initialising Kodi cannot focus (and logs an error); the default control, the
        # first menu button, has the focus anyway.
        if not self._starting:
            super().setFocusId(control_id)

    def _delayed_focus(self, control_id, delay=0.5):
        """Focus a control a moment later; only the latest request counts (an older one would undo a newer screen's)."""
        self._focus_serial += 1
        serial = self._focus_serial

        def set_focus():
            time.sleep(delay)
            while self._starting:                    # the window is still being filled: Kodi cannot focus yet
                time.sleep(0.02)
            try:
                if serial == self._focus_serial and not self._closing and not self.getProperty(EXIT_PROPERTY):
                    self.setFocusId(control_id)
            except Exception:
                pass
        threading.Thread(target=set_focus, daemon=True).start()

    def _show_toast(self, message, toast_type='info', duration=3.0):
        icon = xbmcgui.NOTIFICATION_ERROR if toast_type == 'error' else xbmcgui.NOTIFICATION_INFO
        xbmcgui.Dialog().notification('Milionář', message, icon, int(duration * 1000))

    def _close_game_dialog(self):
        if self._closing:
            return
        self._closing = True
        self.close()

    def leave(self, intent):
        """The player leaves the game: back to the Home screen (the titles are there), or to its main menu."""
        self._close_game_dialog()
        if intent == 'home':
            xbmc.executebuiltin('SetFocus({})'.format(HOME_MENU_ID))

    # ---- vstup ----

    def onClick(self, controlId):
        intent = self._design.clicks.get(controlId)
        if intent in ('mode_millionaire', 'mode_story'):
            self._switch_mode(intent[5:])
        elif intent == 'settings':
            self.open_settings()
        elif intent and intent.startswith('set_') and self.getProperty(SETTINGS_PROPERTY):
            row = self._setting_row(controlId)
            if row:
                game_settings.cycle(row)
                self._publish_settings(row)
        elif intent == 'back' and self._design.modern:
            self._request_back()
        elif intent == 'exit_confirm':
            self._return_to_title()
        elif intent == 'exit_stay':
            self._dismiss_exit()
        elif intent and self._adapter:
            self._adapter.act(intent)

    def onAction(self, action):
        if self.getProperty(SETTINGS_PROPERTY):
            if action.getId() in (ACTION_PREVIOUS_MENU, ACTION_NAV_BACK):
                self.close_settings()
                return
            if action.getId() in (ACTION_MOVE_LEFT, ACTION_MOVE_RIGHT):
                row = self._setting_row(self.getFocusId())
                if row:                                # left / right change the value of the focused row
                    game_settings.step(row, 1 if action.getId() == ACTION_MOVE_RIGHT else -1)
                    self._publish_settings(row)
                return
        if action.getId() in (ACTION_PREVIOUS_MENU, ACTION_NAV_BACK):
            if self.getProperty(EXIT_PROPERTY):
                self._dismiss_exit()
            elif self._design.modern and self._adapter and self._adapter.screen() != 'title':
                self._request_back()
            else:
                self._close_game_dialog()
            return
        super().onAction(action)

    def onFocus(self, controlId):
        row = self._setting_row(controlId)
        if row and self.getProperty(SETTINGS_PROPERTY):
            self._publish_settings(row)

    # ---- settings of the game (the title card): the very addon settings, see wcs/games/game_settings.py ----

    def open_settings(self):
        if not self._adapter or self._adapter.screen() != 'title':
            return
        first = game_settings.rows_for(self.mode)[0]
        self._publish_settings(first)
        self.setProperty(SETTINGS_PROPERTY, 'true')
        self.setFocusId(game_settings.CONTROLS[first.key])

    def close_settings(self):
        self.clearProperty(SETTINGS_PROPERTY)
        self.setFocusId(SETTINGS_BUTTON)
        if self.mode == 'story':
            self._adapter.start()                    # refresh the count chips from the same saved setting

    def _setting_row(self, control_id):
        row = game_settings.BY_CONTROL.get(control_id)
        return row if row in game_settings.rows_for(self.mode) else None

    def _switch_mode(self, mode):
        if not self._adapter or self._adapter.screen() != 'title' or self.getProperty(SETTINGS_PROPERTY):
            return
        if mode == 'story' and not self.story_available:
            return
        xbmcaddon.Addon().setSetting('game_last_mode', mode)
        if self.mode == mode:
            return
        self.mode = mode
        self._return_to_title()

    def _publish_settings(self, row):
        """The values of all rows (read from the addon settings again) and the card of the focused row."""
        for name, value in game_settings.properties(rows=game_settings.rows_for(self.mode)).items():
            self.setProperty(name, value)
        name, value, description = game_settings.describe(row)
        self.setProperty('WCS.Game.SetTitle', name)
        self.setProperty('WCS.Game.SetValue', value)
        self.setProperty('WCS.Game.SetDesc', description)
        self.setProperty('WCS.Game.SetChoices', game_settings.choices(row))

    def _request_back(self):
        """Leaving a started game needs confirmation; setup pages return directly."""
        if self._adapter and self._design.confirm_exit and self._adapter.screen() in (
                'loading', 'question', 'feedback'):
            self.setProperty(EXIT_PROPERTY, 'true')
            self.setFocusId(EXIT_FOCUS)
        else:
            self._return_to_title()

    def _return_to_title(self):
        """Start a fresh menu in this modal; retired workers cannot update its UI."""
        if not self._adapter:
            return
        self._dispose_adapter()
        self.clearProperty(EXIT_PROPERTY)
        self.onInit()
        self._delayed_focus(self._design.menu_primary)

    def _dismiss_exit(self):
        self.clearProperty(EXIT_PROPERTY)
        if self._adapter:
            self.setFocusId(self._adapter.focus_target())

    def close(self):
        self._closing = True
        self._dispose_adapter()
        probe = _game_probe()
        if probe:
            probe.detach(self)
        super().close()

    def _dispose_adapter(self):
        """One retirement path, including native Kodi closure and mode/title changes."""
        self._focus_serial += 1
        adapter, self._adapter = self._adapter, None
        if adapter:
            try:
                adapter.retire()
            except Exception as exc:
                xbmc.log('[plugin.video.milionar] MilionarGameModal: retirement failed: {}'.format(exc), xbmc.LOGWARNING)
            adapter.cleanup()
