# -*- 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: Guido Amoruso <guidonte@fluendo.com>
#          Olivier Tilloy <olivier@fluendo.com>

from elisa.core import common
from elisa.core.media_uri import MediaUri
from elisa.core.utils import defer
from elisa.core.utils.i18n import install_translation
from elisa.core.action import ContextualAction

from elisa.plugins.base.models.media import PlaylistModel
from elisa.plugins.base.models.image import ImageModel

from elisa.plugins.poblesec.actions import OpenControllerAction

from elisa.plugins.database.models import File, Image, PICTURES_SECTION, \
                                          TVEpisode

from twisted.internet import task

import datetime
import os.path
from random import shuffle


_ = install_translation('database')


class EnqueueAndPlayMusicMixin(object):

    def enqueue_and_play(self, tracks, index=0):
        """
        Generic method to call for enqueuing and playing music.

        We pass a list of tracks that we want to enqueue and, eventually, the
        index of a track inside this list to be played first.
        """
        playlist = PlaylistModel()
        playlist.extend(tracks)
        poblesec = self.controller.frontend.retrieve_controllers('/poblesec')[0]
        player = poblesec.music_player.player
        player.set_playlist(playlist)
        poblesec.show_music_player()
        player.play_at_index(index)


class EnqueueAndPlayVideosMixin(object):

    def enqueue_and_play(self, videos, index=0):
        """
        Generic method to call for enqueuing and playing videos.

        We pass a list of videos that we want to enqueue and, eventually, the
        index of a video inside this list to be played first.
        """
        playlist = PlaylistModel()
        playlist.extend(videos)
        poblesec = self.controller.frontend.retrieve_controllers('/poblesec')[0]
        player = poblesec.video_player.player
        player.set_playlist(playlist)
        poblesec.show_video_player()
        player.play_at_index(index)


class EnqueueAndPlayPicturesMixin(object):

    def _iterate(self, playlist, pictures):
        for picture in pictures:
            if isinstance(picture, ImageModel):
                playlist.append(picture)
            else:
                # Instantiate an ImageModel from a database Image model
                image = ImageModel()
                if hasattr(picture, "thumbnail") and picture.thumbnail:
                    thumbnail_uri = MediaUri('file://' + picture.thumbnail)
                    image.references.append(thumbnail_uri)
                image.references.append(MediaUri('file://' + picture.file_path))
                image.title = os.path.basename(picture.file_path)
                if picture.orientation is not None:
                    image.orientation = picture.orientation
                playlist.append(image)

            yield None

    def build_playlist(self, pictures):
        playlist = []
        dfr = task.coiterate(self._iterate(playlist, pictures))
        dfr.addCallback(lambda result: playlist)
        return dfr

    def enqueue_and_play(self, pictures, index=0):
        poblesec = self.controller.frontend.retrieve_controllers('/poblesec')[0]
        player = poblesec.slideshow_player.player
        player.set_playlist(pictures)
        poblesec.show_slideshow_player()
        player.start_slideshow(index)


class ShuffleContextualAction(ContextualAction, EnqueueAndPlayMusicMixin):

    name = 'shuffle'

    def execute(self, item):
        def got_tracks(tracks):
            shuffle(tracks)
            return self.enqueue_and_play(tracks)

        dfr = item.get_tracks()
        dfr.addCallback(got_tracks)
        return dfr


class InformationAction(ContextualAction):
    # Detailed information about an item (generic action, should be subclassed)

    name = 'info'
    label = _('Info')


class MoreOptionsAction(OpenControllerAction):
    # More options associated to an item (generic action, should be subclassed)

    name = 'more'
    label = _('More')
    path = ''

    def __init__(self, controller):
        OpenControllerAction.__init__(self, controller, self.path)


class PlayArtistAction(ContextualAction, EnqueueAndPlayMusicMixin):
    # Enqueue all tracks of all albums of the artist and start playing the
    # first one.
    # This is the default action for artists.

    def execute(self, item):
        # FIXME: using get_tracks may not be a good idea, albums will not be
        # ordered by name and tracks by number.
        dfr = item.get_tracks()
        dfr.addCallback(self.enqueue_and_play)
        return dfr


class ArtistTracksShuffleAction(ShuffleContextualAction):
    label = _('Shuffle and Play All Tracks')


class ListArtistAlbumsAction(OpenControllerAction):
    # List all the albums of a given artist

    name = 'list'
    label = _('List')

    def execute(self, item):
        return self.open_controller(self.path, item.name, artist=item)


class ArtistInformationAction(InformationAction):
    # Detailed information on an album

    # TODO: implement me
    pass


class ArtistMoreOptionsAction(MoreOptionsAction):
    # More options associated to an artist
    path = '/poblesec/database/artist/more'

    def execute(self, item):
        return self.open_controller(self.path, item.name, artist=item)


class PlayAlbumAction(ContextualAction, EnqueueAndPlayMusicMixin):
    # Enqueue all tracks in the album and start playing the first one.
    # This is the default action for music albums.

    def execute(self, item):
        dfr = item.get_tracks()
        dfr.addCallback(self.enqueue_and_play)
        return dfr


class ListAlbumTracksAction(OpenControllerAction):
    # List all tracks in a given album
    name = 'list'
    label = _('Tracks List')

    def __init__(self, controller):
        path = '/poblesec/database/album/list'
        super(ListAlbumTracksAction, self).__init__(controller, path)

    def execute(self, item):
        return self.open_controller(self.path, item.name, album=item)


class AlbumInformationAction(InformationAction):
    # Detailed information on an album

    # TODO: implement me
    pass


class AlbumMoreOptionsAction(MoreOptionsAction):
    # More options associated to an album
    path = '/poblesec/database/album/more'

    def execute(self, item):
        return self.open_controller(self.path, item.name, album=item)


class AlbumTracksShuffleAction(ShuffleContextualAction):
    label = _('Shuffle and Play Tracks')


class PlayTrackAction(ContextualAction, EnqueueAndPlayMusicMixin):
    """
    Enqueue and start playing a single track.
    This is the default action for the Generic Track screen.
    """

    def execute(self, item):
        self.enqueue_and_play([item])
        return defer.succeed(None)

class PlaySeveralTracksAction(ContextualAction, EnqueueAndPlayMusicMixin):
    """
    Enqueue the entire controller's list of tracks and start playing a single track.
    This is the default action for the Album Tracks screen, for instance.
    """

    def execute(self, item):
        index = self.controller.model.index(item)
        self.enqueue_and_play(self.controller.model, index)
        return defer.succeed(None)

class PlayVideoAction(ContextualAction, EnqueueAndPlayVideosMixin):
    """
    Enqueue and start playing a single video.
    This is the default action for videos.
    """

    def execute(self, item):
        self.enqueue_and_play([item])
        return defer.succeed(None)

class PlaySeveralVideosAction(ContextualAction, EnqueueAndPlayVideosMixin):
    """
    Enqueue the entire controller's list of videos and start playing a single video.
    This is the default action for the TV Series, for instance.
    """

    def execute(self, item):
        index = self.controller.model.index(item)
        self.enqueue_and_play(self.controller.model, index)
        return defer.succeed(None)

class OpenMovieSynopsisAction(OpenControllerAction):

    """
    Open the synopsis of a movie.
    """

    name = 'info'

    def __init__(self, controller):
        path = '/poblesec/database/movie/synopsis'
        super(OpenMovieSynopsisAction, self).__init__(controller, path)

    def execute(self, item):
        return self.open_controller(self.path, _('Synopsis'), movie=item,
                                    movies=self.controller.model)


class VideoMoreOptionsAction(MoreOptionsAction):
    # More options associated to a video

    # TODO: implement me
    pass


class ListSeasonEpisodesAction(OpenControllerAction):
    # List all the episodes in a TV show season.

    name = 'list'
    label = _('Episode List')

    def __init__(self, controller):
        path = '/poblesec/database/tvseason/list'
        super(ListSeasonEpisodesAction, self).__init__(controller, path)

    def _get_title(self, item):
        def got_show(show):
            return "%s Season %d" % (show.name, item.number)

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

    def execute(self, item):
        def open_controller(title):
            return self.open_controller(self.path, title, season=item)

        dfr = self._get_title(item)
        dfr.addCallback(open_controller)
        return dfr


class TVSeasonMoreOptionsAction(MoreOptionsAction):
    # More options associated to a TV show season.
    path = '/poblesec/database/tvseason/more'

    def _get_title(self, item):
        def got_show(show):
            return "%s Season %d" % (show.name, item.number)

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

    def execute(self, item):
        def open_controller(title):
            return self.open_controller(self.path, title, season=item)

        dfr = self._get_title(item)
        dfr.addCallback(open_controller)
        return dfr


class PlayTVSeasonAction(ContextualAction, EnqueueAndPlayVideosMixin):
    # Enqueue all episodes in the season and start playing the first one.

    label = _('Play Entire Season')

    def execute(self, item):
        dfr = item.episodes.order_by(TVEpisode.number)
        dfr.addCallback(lambda result_set: result_set.all())
        dfr.addCallback(self.enqueue_and_play)
        return dfr


class OpenTVEpisodeSynopsisAction(OpenControllerAction):

    """
    Open the synopsis of a TV episode.
    """

    name = 'info'

    def __init__(self, controller):
        path = '/poblesec/database/tvepisode/synopsis'
        super(OpenTVEpisodeSynopsisAction, self).__init__(controller, path)

    def execute(self, item):
        return self.open_controller(self.path, _('Synopsis'), episode=item,
                                    episodes=self.controller.model)


class PlayPictureAction(ContextualAction, EnqueueAndPlayPicturesMixin):
    # Enqueue all images and start the slideshow at the selected one.
    # This is the default action for photo controllers.

    def execute(self, item):
        index = self.controller.model.index(item)
        dfr = self.build_playlist(self.controller.model)
        dfr.addCallback(self.enqueue_and_play, index)
        return dfr


class PictureMoreOptionsAction(MoreOptionsAction):
    # More options associated to a picture

    # TODO: implement me
    pass


class PlayPictureAlbumAction(ContextualAction, EnqueueAndPlayPicturesMixin):
    # Enqueue all images in the album and start the slideshow at the first one.
    # This is the default action for photo album controllers.

    def execute(self, item):
        def order(result_set):
            result_set.order_by(Image.shot_time, Image.file_path)
            return result_set.all()

        dfr = item.photos.find()
        dfr.addCallback(order)
        dfr.addCallback(self.build_playlist)
        dfr.addCallback(self.enqueue_and_play)
        return dfr


class ListAlbumPicturesAction(OpenControllerAction):
    # List all the pictures in a given album

    name = 'list'
    label = _('List')

    def __init__(self, controller):
        path = '/poblesec/database/photo/list'
        super(ListAlbumPicturesAction, self).__init__(controller, path)

    def execute(self, item):
        return self.open_controller(self.path, item.name, album=item)


class PictureAlbumMoreOptionsAction(MoreOptionsAction):
    # More options associated to a picture album

    # TODO: implement me
    pass


class PlayPictureTimeAction(ContextualAction, EnqueueAndPlayPicturesMixin):
    # Enqueue all images in a given time frame (year/month) and start the
    # slideshow at the first one.
    # This is the default action for the photo times controller.

    def execute(self, item):
        store = common.application.store
        try:
            last = datetime.datetime(item.year, item.month + 1, 1)
        except ValueError:
            # We reached the end of the year, skip to January of the following
            # year.
            last = datetime.datetime(item.year + 1, 1, 1)

        def order(result_set):
            result_set.order_by(Image.shot_time, Image.file_path)
            return result_set.all()

        dfr = store.find(Image, Image.section == PICTURES_SECTION,
                         Image.shot_time >= item,
                         Image.shot_time < last,
                         Image.file_path == File.path, File.hidden == False)
        dfr.addCallback(order)
        dfr.addCallback(self.build_playlist)
        dfr.addCallback(self.enqueue_and_play)
        return dfr


class ListPictureTimeAction(OpenControllerAction):
    # List all the pictures in a given album

    name = 'list'
    label = _('List')

    def __init__(self, controller):
        path = '/poblesec/database/photo/by_month'
        super(ListPictureTimeAction, self).__init__(controller, path)

    def execute(self, item):
        try:
            # NOTE: This is a strftime format
            title = item.strftime(_('%B, %Y'))
        except TypeError:
            # strftime in python <=2.5 requires a str
            title = item.strftime(_('%B, %Y').encode('utf-8', 'replace'))

        return self.open_controller(self.path, title,
                                    year=item.year, month=item.month)
