# -*- 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.

from elisa.core.input_event import *
from elisa.core.media_uri import MediaUri
from elisa.core.common import application
from elisa.core.utils.i18n import install_translation
from elisa.core.utils.text import name_to_shortcut

from elisa.plugins.poblesec.base.hierarchy import HierarchyController
from elisa.plugins.poblesec.base.list import GenericListViewMode
from elisa.plugins.poblesec.base.preview_list import \
    MenuItemPreviewListController
from elisa.plugins.poblesec.base.coverflow import \
    ImageWithReflectionCoverflowController
from elisa.plugins.poblesec.base.grid import GridItemGridController
from elisa.plugins.poblesec.section import SectionMenuController
from elisa.plugins.poblesec.link import Link
from elisa.plugins.poblesec.base.list_switcher import ListSwitcherController

from elisa.plugins.base.models.network import NetworkServiceModel

from elisa.plugins.coherence.upnp_resource import IMAGE_MAGIC

from elisa.plugins.coherence.models import UpnpContainerModel
from elisa.plugins.coherence.models import UpnpItemModel

from elisa.plugins.base.models.image import ImageModel
from elisa.plugins.base.models.audio import TrackModel, AlbumModel, ArtistModel
from elisa.plugins.base.models.video import VideoModel

from elisa.plugins.base.messages.network import NetworkServiceAppearedMessage
from elisa.plugins.base.messages.network import NetworkServiceDisappearedMessage

from elisa.core.resource_manager import NoMatchingResourceProvider


from twisted.internet import defer, task
import copy

_ = install_translation('poblesec')


"""
TODO
 - images slideshow
 - actually activate item ordering
 - on the fly updating of new/removed shares
 - playing of audio and video
 - media type filtering / item icons
 - item ordering
 - check whether the share is actually accessible
"""


class NetworkSharesList(SectionMenuController):

    def initialize(self):
        dfr = super(NetworkSharesList, self).initialize()
        self.shares = []
        return dfr

    def set_frontend(self, frontend):
        super(NetworkSharesList, self).set_frontend(frontend)
        return self.list_network_shares()

    def list_network_shares(self):
        # Keep 3 slashes
        filtering = 'file,audio,upnp'
        uri = MediaUri("network-services:///?filter=%s" % filtering)
        model, dfr = application.resource_manager.get(uri, None)

        def do_list_network_shares(model):
            for share in model.services:
                self._add_share(share)

            application.bus.register(self._share_added_cb, NetworkServiceAppearedMessage)
            application.bus.register(self._share_removed_cb,
                                     NetworkServiceDisappearedMessage)
            return model

        def do_list_network_shares_error(err, *args):
            return err

        dfr.addCallback(do_list_network_shares)
        dfr.addErrback(do_list_network_shares_error)

        return dfr

    def _add_share(self, share):
        link = Link()
        link.controller_path = '/poblesec/network'
        link.controller_args = {'uri': share.elisa_uri}
        link.label = share.name
        
        # some protocols should be treated differently
        scheme = share.elisa_uri.scheme
        if scheme == 'daap':
            # we need to use a different controller for daap
            link.controller_path = '/poblesec/music/daap'
        elif scheme == 'smb':
            # SMB shares look best capitalized
            link.label = share.name.capitalize()
            
        link.icon = "elisa.plugins.poblesec.glyphs.small.devices_shares"

# FIXME: activate this code (alphabetic ordering of shares) when the list
# refreshing is fixed.
#        i = 0
#        for i, s in enumerate(self.shares):
#            if s.name.lower() > share.name.lower():
#                break
#        self.model.insert(i, link)
#        self.shares.insert(i, share)

        resource_manager = application.resource_manager
        try:
            # TODO: expose this in resource_manager.
            resource_provider = resource_manager._get_resource_provider(share.elisa_uri)
        except NoMatchingResourceProvider, err:
            self.warning(err)
        else:
            # show only the shares we have a resource provider for
            self.model.append(link)
            self.shares.append(share)

    def _share_added_cb(self, msg, sender):
        self.debug("New share appeared: %s" % msg.model.name)
        # FIXME: loosing a deferred
        self._add_share(msg.model)

    def _share_removed_cb(self, msg, sender):
        uids = [s.uid for s in self.shares]

        if msg.uid in uids:
            index = uids.index(msg.uid)
            share = self.shares.pop(index)
            self.debug("Removing share '%s'" % share.name)
            self.model.pop(index)
        else:
            self.warning("Unknown share disappeared: '%s'" % msg.uid)

    def clean(self):
        dfr = super(NetworkSharesList, self).clean()
        application.bus.unregister(self._share_added_cb)
        application.bus.unregister(self._share_removed_cb)
        return dfr


class NetworkShareBrowser(HierarchyController):
    empty_label = _("This shared folder does not contain any multimedia files")

    def initialize(self, uri=None):
        dfr = super(NetworkShareBrowser, self).initialize()

        self.uri = uri

        def upnp_resource_loaded(resource):
            items = resource.items

            self.model[:] = sorted(items, key=lambda i: i.name.lower())

            return self

        def smb_resource_loaded(resource):
            items = resource.servers

            self.model[:] = sorted(items, key=lambda i: i.name.lower())

            return self

        def general_resource_loaded(resource):
            items = resource.files

            self.model[:] = sorted(items, key=lambda i: i.name.lower())

            return self

        def load_resource(controller):
            # we assume that self.uri is a directory
            resource, dfr_resource = application.resource_manager.get(self.uri)
            return dfr_resource

        if self.uri != None:
            dfr.addCallback(load_resource)
            if self.uri.scheme == 'upnp':
                loaded_cb = upnp_resource_loaded
            elif self.uri.scheme == 'smb':
                loaded_cb = smb_resource_loaded
            else:
                loaded_cb = general_resource_loaded

            dfr.addCallback(loaded_cb)

        return dfr

    def node_clicked(self, widget, item):
        if isinstance(item, (NetworkServiceModel, UpnpContainerModel)):
            controllers = self.frontend.retrieve_controllers('/poblesec/browser')
            browser = controllers[0]

            if item.elisa_uri.scheme == 'file':
                path = '/poblesec/filesystem'
            else:
                path = self.path
            dfr = browser.history.append_controller(path,
                                              item.name,
                                              uri=item.elisa_uri)
        elif isinstance(item, ImageModel):
            dfr = self.play_image(item)
        elif isinstance(item, VideoModel):
            self.play_audio_video(item, 'video')
            dfr = defer.succeed(True)
        elif isinstance(item, TrackModel):
            self.play_audio_video(item, 'music')
            dfr = defer.succeed(True)
        return dfr

    def play_image(self, item):
        controllers = self.frontend.retrieve_controllers('/poblesec/slideshow_player')
        slideshow_controller = controllers[0]
        playlist = []

        def iterate_model():
            for item in self.model:
                if isinstance(item, ImageModel):
                    image = ImageModel()
                    uri = MediaUri(item.playable_uri + IMAGE_MAGIC)
                    image.references.append(uri)
                    image.title = item.name
                    playlist.append(image)
                yield item

        def playlist_filled(result):
            index = self.model.index(item)
            slideshow_controller.player.set_playlist(playlist, index)

            controllers = self.frontend.retrieve_controllers('/poblesec')
            main = controllers[0]
            main.show_slideshow_player()

        dfr = task.coiterate(iterate_model())
        dfr.addCallback(playlist_filled)
        return dfr

    def play_audio_video(self, item, player):
        # player should be one of ('music', 'video')
        controllers = self.frontend.retrieve_controllers('/poblesec/%s_player' % player)
        player_controller = controllers[0]

        # enqueue and play the clicked item
        item.playable_model.title = item.name
        player_controller.player.play_model(item.playable_model)

        controllers = self.frontend.retrieve_controllers('/poblesec')
        main = controllers[0]
        eval('main.show_%s_player()' % player)

        # enqueue all the following items of the container
        index = self.model.index(item)
        for item in self.model[index+1:]:
            if isinstance(item, UpnpContainerModel):
                continue
            item.playable_model.title = item.name
            player_controller.player.enqueue_to_playlist(item.playable_model)

    def _disconnect_metadata(self, metadata, handler_id):
        metadata.notifier.disconnect(handler_id)


class NetworkViewMode(GenericListViewMode):

    """
    Implementation of the common view modes API.
    """

    itemclass_to_resource = {
        UpnpContainerModel: 'elisa.plugins.poblesec.glyphs.small.devices_shares',
        TrackModel: 'elisa.plugins.poblesec.glyphs.small.music',
        VideoModel: 'elisa.plugins.poblesec.glyphs.small.video',
        ImageModel: 'elisa.plugins.poblesec.glyphs.small.pictures',
        AlbumModel: 'elisa.plugins.poblesec.glyphs.small.music',
        ArtistModel: 'elisa.plugins.poblesec.glyphs.small.devices_shares',
        NetworkServiceModel: 'elisa.plugins.poblesec.glyphs.small.devices_shares',
    }

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

    def get_default_image(self, item):
        resource = None
        for klass, value in self.itemclass_to_resource.iteritems():
            if isinstance(item, klass):
                resource = value
                break
        return resource

    def get_image(self, item, theme):
        return None

    def get_preview_image(self, item, theme):
        return None


class NetworkPreviewListController(NetworkShareBrowser, MenuItemPreviewListController):
    view_mode = NetworkViewMode
    fastscroller_enabled = True

    def get_shortcut_for_item(self, item):
        return name_to_shortcut(item.name)


class NetworkCoverflowController(NetworkShareBrowser, ImageWithReflectionCoverflowController):
    view_mode = NetworkViewMode


class NetworkGridController(NetworkShareBrowser, GridItemGridController):
    view_mode = NetworkViewMode


class NetworkListSwitcherController(ListSwitcherController):
    modes = [NetworkPreviewListController,
             NetworkCoverflowController,
             NetworkGridController]
    default_mode = NetworkPreviewListController
