# -*- 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.media_uri import MediaUri
from elisa.core import common
from elisa.core.utils.i18n import install_translation
from elisa.core.utils import defer
from elisa.core.utils.text import name_to_shortcut
from elisa.core.action import ContextualAction

from elisa.plugins.base.models.file import DirectoryModel
from elisa.plugins.base.models.device import VolumeModel

from elisa.plugins.base.messages.device import NewDeviceDetected, \
                                               DeviceRemoved

from elisa.plugins.poblesec.actions import OpenControllerAction
from elisa.plugins.poblesec.base.preview_list import \
    ListItemWidgetWithActions, MenuItemPreviewListController
from elisa.plugins.poblesec.base.list_switcher import ListSwitcherController
from elisa.plugins.poblesec.filesystem import FilesystemController, \
                                              FilesystemViewMode, \
                                              OpenFolderOrPlayFileAction
from elisa.plugins.poblesec.base.preview_list import PreviewListController

import os.path
import platform

from twisted.internet import task


_ = install_translation('poblesec')

HOME_URI = MediaUri({'scheme': 'file', 'path': os.path.expanduser('~')})


class AddRemoveFolderAction(ContextualAction):

    """
    Contextual action associated to folders/volumes to add/remove them from the
    list of media sources.
    """

    def __init__(self, controller, remove_from_model=False):
        # remove_from_model == True -> when removed from the media sources, the
        # corresponding item will also be removed from the controller's model.
        super(AddRemoveFolderAction, self).__init__(controller)
        self.remove_from_model = remove_from_model

    def _remove_from_model(self, result, item):
        self.controller.model.remove(item)

    def execute(self, item):
        removing = item.is_a_media_source
        # The actual logic is delegated to self.controller's add_remove method.
        dfr = self.controller.add_remove(item)
        if removing and self.remove_from_model:
            dfr.addCallback(self._remove_from_model, item)
        return dfr


class _ConditionalListItemWidgetWithActions(ListItemWidgetWithActions):

    # Do not [create and show|hide and destroy] contextual actions for files.
    # FIXME: this is obviously a hack. Consider really hard re-factoring the
    # contextual actions in a clean way rather than re-using this.

    def _create_and_show_actions(self):
        if not isinstance(self.item, (DirectoryModel, VolumeModel)):
            return

        action = self._contextual_actions[0]

        if self.item.is_a_media_source:
            action.name = 'remove'
        else:
            action.name = 'add'

        super(_ConditionalListItemWidgetWithActions,
              self)._create_and_show_actions()

    def _destroy_and_hide_actions(self):
        if not isinstance(self.item, (DirectoryModel, VolumeModel)):
            return
        super(_ConditionalListItemWidgetWithActions,
              self)._destroy_and_hide_actions()


class DirectorySettingsController(FilesystemController):

    """
    A filesystem controller that adds a contextual action to add/remove
    folders/volumes to the list of media sources.
    """

    node_widget = _ConditionalListItemWidgetWithActions

    def create_actions(self):
        default = OpenFolderOrPlayFileAction(self,
                                             '/poblesec/directory_settings')
        add_remove = AddRemoveFolderAction(self)
        return default, [add_remove]

    def add_remove(self, item):
        uri = self.item_to_uri(item)
        action = self._contextual_actions[0]
        media_directories = common.application.media_directories

        if item.is_a_media_source:
            # Remove the URI from the media sources
            for media_type in media_directories.directories:
                media_directories.remove_directory(uri, media_type)
            media_directories.refresh_directory_list()
            item.is_a_media_source = False
            action.name = 'add'
            self.model[self.model.index(item)] = item
            if platform.system() == 'Windows':
                path = uri.path
            else:
                # avoid double /
                path = uri.path[1:]
            source_uri = MediaUri('media_scanner://localhost/%s/' % path)
            dfr = common.application.resource_manager.delete(source_uri)
        else:
            # Add the URI to the media sources
            self._show_add_popup(item, action)
            dfr = defer.succeed(None)
        return dfr

    def _show_add_popup(self, item, action):
        main = self.frontend.retrieve_controllers('/poblesec')[0]
        title = _('ADD SOURCE')
        subtitle = _('What Kind Of Media Does It Contain?')
        text = _("Before we add this source, we require you to select what " \
                 "kind of media it contains. Please select from one of the 3 " \
                 "available options below. Alternatively select 'Cancel' to " \
                 "return to source list and continue browsing.")
        buttons = [(_('Cancel'), main.hide_popup),
                   (_('Music'), lambda: self._add_source_to_media_type('music', item, action, main)),
                   (_('Video'), lambda: self._add_source_to_media_type('video', item, action, main)),
                   (_('Photos'), lambda: self._add_source_to_media_type('pictures', item, action, main)),
                  ]
        main.enqueue_popup(title, subtitle, text, buttons)

    def _add_source_to_media_type(self, media_type, item, action, main):
        media_directories = common.application.media_directories
        uri = self.item_to_uri(item)
        media_directories.add_directory(uri, media_type)
        media_directories.refresh_directory_list()
        item.is_a_media_source = True
        action.name = 'remove'
        self.model[self.model.index(item)] = item
        # Return a deferred that we are loosing anyway.
        return main.hide_popup()


class DirectorySettingsPreviewListController(DirectorySettingsController,
                                             MenuItemPreviewListController):
    view_mode = FilesystemViewMode

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


class ThisComputerBrowserController(DirectorySettingsController):

    def populate_model(self):
        model = []
        media_directories = common.application.media_directories

        # Home folder
        home = DirectoryModel()
        home.name = _('Home')
        home.uri = HOME_URI
        home.is_a_media_source = media_directories.is_a_media_source(home.uri)
        model.append(home)

        # Fixed devices
        def get_devices():
            def get_removable(all_devices):
                uri = MediaUri('volumes://localhost/?filter=removable')
                devices, dfr = common.application.resource_manager.get(uri)
                return dfr

            def filter_out_removable(removable_devices, all_devices):
                # FIXME: make this asynchronous using task.coiterate
                for device in removable_devices.devices:
                    try:
                        all_devices.devices.remove(device)
                    except:
                        pass

            uri = MediaUri('volumes://localhost/?filter=file')
            all_devices, dfr = common.application.resource_manager.get(uri)
            dfr.addCallback(get_removable)
            dfr.addCallback(filter_out_removable, all_devices)
            dfr.addCallback(lambda result: all_devices)
            return dfr

        def add_volumes(devices, model):
            # FIXME: make this asynchronous using task.coiterate
            for device in devices.devices:
                if isinstance(device, VolumeModel) and \
                    device.mount_point is not None:
                    uri = self.item_to_uri(device)
                    device.is_a_media_source = \
                        media_directories.is_a_media_source(uri)
                    model.append(device)

        def error_getting_devices(failure):
            self.warning('Failed to retrieve the list of devices: %s.' % \
                         failure)
            return failure

        def connect_to_bus(result):
            bus = common.application.bus
            bus.register(self._device_added_cb, NewDeviceDetected)
            bus.register(self._device_removed_cb, DeviceRemoved)

        dfr = get_devices()
        dfr.addCallbacks(add_volumes, error_getting_devices,
                         callbackArgs=(model,))
        dfr.addCallback(connect_to_bus)
        dfr.addCallback(lambda result: model)
        return dfr

    def _device_added_cb(self, message, sender):
        volume = message.model
        if volume.protocol == 'file' and volume.mount_point is not None:
            uri = self.item_to_uri(volume)
            volume.is_a_media_source = \
                common.application.media_directories.is_a_media_source(uri)
            self.model.append(volume)

    def _device_removed_cb(self, message, sender):
        for volume in self.model:
            if isinstance(volume, VolumeModel) and volume.udi == message.udi:
                self.model.remove(volume)
                break

    def item_activated(self, item):
        if isinstance(item, VolumeModel):
            uri = MediaUri({'scheme': 'file', 'path': item.mount_point})
        else:
            uri = item.uri

        action = OpenControllerAction(self, '/poblesec/directory_settings')
        return action.open_controller(action.path, item.name, uri=uri,
                                      media_type=self.media_type)


class ThisComputerPreviewListController(ThisComputerBrowserController,
                                        MenuItemPreviewListController):
    view_mode = FilesystemViewMode
    fastscroller_enabled = False

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


class MediaSourcesController(DirectorySettingsController):

    def populate_model(self):
        model = []

        media_directories = common.application.media_directories
        media_directories.refresh_directory_list(self.media_type)

        if self.media_type is None:
            media_types = ['music', 'video', 'pictures']
        else:
            media_types = [self.media_type]

        def iterate_sources(sources, model):
            for source in sources:
                # Do not add twice the same source.
                already_listed = False
                for item in model:
                    if item.uri.path == source:
                        already_listed = True
                        break
                if not already_listed:
                    item = DirectoryModel()
                    item.uri = MediaUri({'scheme': 'file', 'path': source})
                    item.name = item.uri.label
                    item.is_a_media_source = True
                    model.append(item)
                yield None

        def iterate_media_types(media_types, model):
            for media_type in media_types:
                sources = media_directories.get_directories(media_type)
                yield task.coiterate(iterate_sources(sources, model))

        dfr = task.coiterate(iterate_media_types(media_types, model))
        dfr.addCallback(lambda result: model)
        return dfr

    def create_actions(self):
        default = OpenFolderOrPlayFileAction(self, '/poblesec/filesystem')
        add_remove = AddRemoveFolderAction(self, remove_from_model=True)
        return default, [add_remove]


class MediaSourcesPreviewListController(MediaSourcesController,
                                        MenuItemPreviewListController):
    view_mode = FilesystemViewMode
    fastscroller_enabled = False

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