# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.
#
# Authors: Philippe Normand <philippe@fluendo.com>
#          Olivier Tilloy <olivier@fluendo.com>

import cgi

from elisa.core import common
from elisa.core.media_uri import MediaUri
from elisa.core.utils.i18n import install_translation
from elisa.core.components.resource_provider import ResourceNotFound
from elisa.core.utils import defer

from elisa.plugins.pigment.pigment_controller import PigmentController
from elisa.plugins.pigment.graph.text import Text
from elisa.plugins.pigment.widgets.box import HBox

from elisa.plugins.base.utils import get_and_cache_image, \
                                     get_and_cache_thumbnail

from elisa.plugins.poblesec.base.list_switcher import ListSwitcherController
from elisa.plugins.poblesec.base.preview_list import \
    ListItemWidgetWithActions, DoubleLineMenuItemPreviewListController
from elisa.plugins.poblesec.base.coverflow import \
    ImageWithReflectionCoverflowController
from elisa.plugins.poblesec.base.grid import GridItemGridController
from elisa.plugins.poblesec.base.info_list import InfoListController, \
                                                  ListInfoScreen
from elisa.plugins.poblesec.link import Link
from elisa.plugins.poblesec.widgets.background import WidgetWithBackground
from elisa.plugins.poblesec.widgets.info_screen import TextInfoScreen
from elisa.plugins.poblesec.widgets.button import PanelButton
from elisa.plugins.poblesec.actions import OpenControllerAction

from elisa.plugins.database.models import TVShow, TVSeason, TVEpisode
from elisa.plugins.database.video_controller import VideoController, \
                                                    VideoViewMode

from elisa.plugins.database.actions import ListSeasonEpisodesAction, \
                                           OpenTVEpisodeSynopsisAction, \
                                           PlaySeveralVideosAction, \
                                           PlayVideoAction, \
                                           TVSeasonMoreOptionsAction, \
                                           PlayTVSeasonAction


_ = install_translation('database')


def tvshows_lib_decorator(controller):
    link = Link()
    link.controller_path = '/poblesec/database/tvshows/list'
    link.label = _('TV Shows')
    link.icon = 'elisa.plugins.poblesec.glyphs.small.tv_shows'
    controller.model.append(link)
    return defer.succeed(None)


class AllTVSeasonsController(VideoController):

    empty_label = _('There are no TV shows in this section')

    def populate_model(self):
        def sort_by_show_name(result_set):
            result_set.order_by(TVShow.name, TVSeason.number)
            return result_set.all()

        store = common.application.store
        dfr = store.find(TVSeason, TVSeason.tvshow_id == TVShow.id)
        dfr.addCallback(sort_by_show_name)
        return dfr

    def create_actions(self):
        # Default action: list all the episodes in the season.
        default = ListSeasonEpisodesAction(self)
        # Contextual actions: more options.
        more = TVSeasonMoreOptionsAction(self)
        return default, [more,]


class TVSeasonsViewMode(VideoViewMode):

    def get_label(self, item):
        dfr = item.tvshow
        dfr.addCallback(lambda show: show.name)
        return dfr

    def get_sublabel(self, item):
        return defer.succeed(_("Season %(number)s") % {'number': item.number})

    def get_default_image(self, item):
        return 'elisa.plugins.poblesec.glyphs.small.tv_shows'

    def get_image(self, item, theme):
        try:
            return defer.succeed(item.thumbnail_path)
        except AttributeError:
            def got_uri(poster_uri):
                if poster_uri:
                    return get_and_cache_thumbnail(item, poster_uri)
                else:
                    return None

            dfr = item.poster_uri()
            dfr.addCallback(got_uri)
            return dfr

    def get_preview_image(self, item, theme):
        try:
            return item.thumbnail_path
        except AttributeError:
            return None

    def get_contextual_background(self, item):
        def got_show(show):
            return get_and_cache_image(MediaUri(show.fanart_uri))

        dfr = item.tvshow
        dfr.addCallback(got_show)
        return dfr


class TVSeasonViewMode(VideoViewMode):

    def get_label(self, item):
        return defer.succeed(_("Episode %(number)s") % {'number': item.number})

    def get_sublabel(self, item):
        return defer.succeed(item.name)

    def get_default_image(self, item):
        return 'elisa.plugins.poblesec.glyphs.small.tv_shows'

    def get_image(self, item, theme):
        try:
            return defer.succeed(item.thumbnail_path)
        except AttributeError:
            if item.poster_uri:
                def error_getting_thumbnail(failure, item):
                    failure.trap(ResourceNotFound)
                    return theme.get_resource(self.get_default_image(item))

                dfr = get_and_cache_thumbnail(item, MediaUri(item.poster_uri))
                dfr.addErrback(error_getting_thumbnail)
                return dfr
            else:
                return None

    def get_preview_image(self, item, theme):
        try:
            return item.thumbnail_path
        except AttributeError:
            return None


class TVEpisodeViewMode(TVSeasonViewMode):

    def get_label(self, item):
        return defer.succeed(_("%(number)s. %(name)s") % \
                             {'number': item.number, 'name': item.name})

    def get_sublabel(self, item):

        def got_show(show, season):
            return _("%(name)s - Season %(season)d") % \
                     {'name': show.name, 'season': season.number}

        def got_season(season):
            dfr = season.tvshow
            dfr.addCallback(got_show, season)
            return dfr

        dfr = item.season
        dfr.addCallback(got_season)
        return dfr


class TVSeasonInfoScreen(ListInfoScreen):

    """
    Specialized ListInfoScreen widget to display information or more options for
    a TV show season.
    """

    def pack_captions(self, caption):
        caption.show = Text()
        caption.foreground.pack_start(caption.show, expand=True)
        caption.show.visible = True

        caption.season = Text()
        caption.foreground.pack_start(caption.season, expand=True)
        caption.season.visible = True


class InfoListItemWidgetWithActions(ListItemWidgetWithActions):
    pass


class TVSeasonInfoScreenWithContextualActions(TVSeasonInfoScreen):
    node_widget = InfoListItemWidgetWithActions


class TVSeasonInfoControllerMixin(object):

    def populate_season_info(self, season):
        """
        Populate the generic information of a TV season information screen
        (poster, series name, season number). 
        """
        def set_show_name(show):
            self.info_screen.left_panel.caption.show.label = show.name

        def set_season_number(result):
            self.info_screen.left_panel.caption.season.label = \
                _('Season %(number)s') % {'number': season.number}

        def set_poster(result):
            def _set_poster(thumbnail_path):
                artwork = self.info_screen.left_panel.artwork
                return artwork.foreground.set_from_file_deferred(thumbnail_path)

            try:
                thumbnail_path = season.thumbnail_path
            except AttributeError:
                pass
            else:
                if thumbnail_path is not None:
                    return _set_poster(thumbnail_path)

            def get_thumbnail(poster_uri):
                if poster_uri is not None:
                    return get_and_cache_thumbnail(season, poster_uri)
                else:
                    return None

            dfr = season.poster_uri()
            dfr.addCallback(get_thumbnail)
            dfr.addBoth(_set_poster)
            return dfr

        dfr = season.tvshow
        dfr.addCallback(set_show_name)
        dfr.addCallback(set_season_number)
        dfr.addCallback(set_poster)
        return dfr


class TVSeasonInfoListController(InfoListController, TVSeasonInfoControllerMixin):

    """
    A generic info list controller for a given season of a given TV show.
    """

    view_mode = TVSeasonViewMode
    info_screen_cls = TVSeasonInfoScreen

    def initialize(self, season):
        self.season = season
        return super(TVSeasonInfoListController, self).initialize()

    def populate_info(self):
        return self.populate_season_info(self.season)


class StartRecategorizationAction(OpenControllerAction):

    name = 'recategorize'

    def __init__(self, controller):
        path = '/poblesec/database/video/recategorize/choose_category'
        super(StartRecategorizationAction, self).__init__(controller, path)

    def execute(self, item):
        frontend = self.controller.frontend
        browser = frontend.retrieve_controllers('/poblesec/browser')[0]
        browser.history.set_mark('start_recategorization')
        browser.history.connect('pop_controller', self._controller_popped)
        return self.open_controller(self.path, item.name, video=item)

    # Localized hack to workaround the lack of a generic system to trigger a
    # refresh or update of a list controller's model.
    # This solution should be considered temporary. One of its drawbacks is that
    # the controller's model will be reloaded even if no actual recategorization
    # has been performed (e.g. if the process has been canceled).
    def _controller_popped(self, history, previous, current):
        if current is self.controller:
            history.disconnect_by_func(self._controller_popped)
            self.controller.reload()


class EpisodeListControllerMixin(object):

    empty_label = _('There are no TV episodes in this section')

    def create_actions(self):
        # Default action: play the episode.
        default = PlaySeveralVideosAction(self)
        # Contextual actions: synopsis, recategorize.
        # Note: this is a temporary solution until we set up a "more options"
        # screen, at which point the recategorize action will be moved there.
        synopsis = OpenTVEpisodeSynopsisAction(self)
        recategorize = StartRecategorizationAction(self)
        return default, [synopsis, recategorize]


class TVSeasonEpisodeListController(EpisodeListControllerMixin,
                                    TVSeasonInfoListController):

    """
    The "Episode List" controller for a given season of a given TV show.
    """

    info_screen_cls = TVSeasonInfoScreenWithContextualActions

    def populate_info(self):
        self.info_screen.right_panel.title.foreground.label = _('EPISODE LIST')
        return super(TVSeasonEpisodeListController, self).populate_info()

    def populate_model(self):
        reference_set = self.season.episodes
        dfr = reference_set.order_by(TVEpisode.number)
        dfr.addCallback(lambda rs: rs.all())
        return dfr

    def node_renderer(self, item, widget):
        # Pass the item to the widget so that contextual actions can use it.
        widget.item = item
        widget.item_widget.label.label = \
            _('%(number)02d. %(name)s') % {'number': item.number,
                                           'name': item.name}


class TVSeasonMoreOptionsController(TVSeasonInfoListController):

    """
    The "More Options" controller for a given season of a given TV show.
    """

    def populate_info(self):
        self.info_screen.right_panel.title.foreground.label = _('MORE OPTIONS')
        return super(TVSeasonMoreOptionsController, self).populate_info()

    def populate_model(self):
        play = PlayTVSeasonAction(self)
        episode_list = ListSeasonEpisodesAction(self)
        model = [play, episode_list]
        return defer.succeed(model)

    def node_renderer(self, item, widget):
        widget.item_widget.label.label = item.label

    def item_activated(self, item):
        return item.execute(self.season)


class TVEpisodeSynopsis(TextInfoScreen):

    """
    Specialized information screen widget to display the synopsis of a TV
    episode.
    """

    def pack_captions(self, caption):
        caption.show = Text()
        caption.foreground.pack_start(caption.show, expand=True)
        caption.show.visible = True

        caption.season = Text()
        caption.foreground.pack_start(caption.season, expand=True)
        caption.season.visible = True

    def create_contents(self):
        contents = super(TVEpisodeSynopsis, self).create_contents()

        contents.subtitle = WidgetWithBackground(foreground=HBox())
        contents.subtitle.visible = True
        contents.pack_start(contents.subtitle)

        contents.subtitle.title = Text()
        contents.subtitle.title.visible = True
        contents.subtitle.foreground.pack_start(contents.subtitle.title,
                                                expand=True)
        contents.subtitle.counter = Text()
        contents.subtitle.counter.visible = True
        contents.subtitle.foreground.pack_end(contents.subtitle.counter)
        return contents

    def pack_buttons(self, footer):
        # Play episode button
        footer.play_button = PanelButton()
        footer.play_button.text.label = _('Play Episode')
        footer.play_button.visible = True
        footer.pack_start(footer.play_button, expand=True)


class TVEpisodeSynopsisController(PigmentController, TVSeasonInfoControllerMixin):

    def initialize(self, episode, episodes):
        self.episode = episode
        self.episodes = episodes
        return super(TVEpisodeSynopsisController, self).initialize()

    def set_frontend(self, frontend):
        super(TVEpisodeSynopsisController, self).set_frontend(frontend)
        info_screen = TVEpisodeSynopsis()
        info_screen.visible = True
        self.widget.add(info_screen)
        self.widget.set_focus_proxy(info_screen)
        info_screen.right_panel.set_focus_proxy(info_screen.right_panel.footer)
        self.info_screen = info_screen
        right_panel = info_screen.right_panel
        right_panel.title.foreground.label = _('INFORMATION')
        right_panel.footer.play_button.connect('activated',
                                               self._play_button_cb)
        return self._populate()

    def _populate(self):
        if not self.episode:
            return
        right_panel = self.info_screen.right_panel
        contents = right_panel.contents
        contents.subtitle.title.label = self.episode.name
        episode_index = self.episodes.index(self.episode) + 1
        contents.subtitle.counter.label = _('%(index)02d of %(count)02d') % {'index': episode_index,
                                                                             'count': len(self.episodes)}

        overview = cgi.escape(self.episode.overview)
        if not overview:
            overview = _('No synopsis available.')
        else:
            overview = _('<b>Episode Synopsis</b>\n%(overview)s') % {'overview': overview}
            guests = self.episode.guest_stars
            if guests:
                overview += _('\n\n<b>Guest Stars</b>\n%(guests)s') % {'guests': guests}
        contents.summary.foreground.markup = overview
        dfr = self.episode.season
        dfr.addCallback(self.populate_season_info)
        return dfr

    def _play_button_cb(self, button, *args):
        action = PlayVideoAction(self)
        action.execute(self.episode)


# All TVSeasons
class AllTVSeasonsVerticalWithPreview(AllTVSeasonsController,
                                      DoubleLineMenuItemPreviewListController):
    fastscroller_enabled = False
    view_mode = TVSeasonsViewMode

class AllTVSeasonsCoverflow(AllTVSeasonsController,
                            ImageWithReflectionCoverflowController):
    view_mode = TVSeasonsViewMode

class AllTVSeasonsGrid(AllTVSeasonsController, GridItemGridController):
    view_mode = TVSeasonsViewMode

class AllTVSeasonsListSwitcherController(ListSwitcherController):
    modes = [AllTVSeasonsVerticalWithPreview,
             AllTVSeasonsCoverflow,
             AllTVSeasonsGrid]
    default_mode = AllTVSeasonsVerticalWithPreview
