# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 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 Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

__maintainer__ = 'Benjamin Kampmann <benjamin@fluendo.com>'

from elisa.base_components.media_provider import MediaProvider
from elisa.core.media_file import MediaFile
from elisa.core.media_uri import MediaUri
from elisa.core.utils import misc
from elisa.core import common
import os, re

import gnomevfs
from twisted.internet import defer, threads
from elisa.core.utils import deferred_action

plugin_registry = common.application.plugin_registry
LocalNetworkLocationMessage = plugin_registry.get_component_class('base:local_network_location_message')

class GnomevfsMedia(MediaProvider):

    hidden_file_pattern = re.compile("\..*")

    def __init__(self):
        MediaProvider.__init__(self)
        self.cwd = gnomevfs.URI(os.getcwd())
        self.smb_groups = []
        self._def_action = deferred_action.DeferredActionsManager()

    def _uri_join(self, uri, filename):
        if uri.scheme == 'smb':
            if uri.path == '/':
                # we're browsing SMB workgroups. GnomeVFS doesn't support
                # smb://workgroup/host .. so we remove the workgroup
                # name from the uri
                f_uri = MediaUri('smb://' + filename)
                if filename not in self.smb_groups:
                    self.smb_groups.append(str(f_uri))
            else:
                f_uri = uri.join(filename)
                in_workgroup = self._uri_in_group(f_uri)
                if in_workgroup:
                    f_uri = MediaUri('smb://%s' % filename)
        else:
            f_uri = uri.join(filename)
        return f_uri

    def _uri_in_group(self, f_uri):
        return len(filter(lambda group_uri: str(f_uri).startswith(group_uri),
                          self.smb_groups)) > 0

    def initialize(self):        
        uri = "smb:///"
        action_type = LocalNetworkLocationMessage.ActionType.LOCATION_ADDED
        # FIXME: "Samba shares" should not be shown to the user. Instead,
        # the tree of workgroups should be flattened and all the machines on
        # the local network should be added under "Home network"
        msg = LocalNetworkLocationMessage(action_type, "Samba shares",
                                          'smb', uri,
                                          media_types=['video','audio', 'image'],
                                          theme_icon='network_share_icon')
        # FIXME: Samba support is deactivated because of:
        # 1) random segfaults
        # 2) UI locking issues
        #common.application.bus.send_message(msg)

    def supported_uri_schemes__get(self):
        return {'smb' : 0, 'sftp' : 250, 'http' : 250, 'ftp' : 250}

    def scannable_uri_schemes__get(self):
        return ['sftp', 'http', 'ftp']

    def is_directory(self, uri):
        return self._def_action.enqueue_action(self._blocking_is_directory,
                                              uri)


    def _blocking_is_directory(self, uri):
        is_dir = False
        if str(uri).find('m3u') == -1:
            try:
                info = gnomevfs.get_file_info(str(uri)).type
            except Exception, exc:
                # Sorry, But I can't figure out how all the possible gnomevfs
                # exceptions are named....
                # ... you lazy
                pass
            else:
                if info == gnomevfs.FILE_TYPE_DIRECTORY:
                    is_dir = True
                elif info == gnomevfs.FILE_TYPE_SYMBOLIC_LINK:
                    try:
                        gnomevfs.open_directory(str(uri))
                    except:
                        pass
                    else:
                        is_dir = True
                    
        return is_dir

    def has_children_with_types(self, uri, media_types):
        return self._def_action.enque_action(self._blocking_has_children_with_types, uri, media_types)


    def _blocking_has_children_with_types(self, uri, media_types):
        has_children = False
        if self._blocking_is_directory(uri):
            try:
                contents = gnomevfs.open_directory(str(uri))
            except Exception, error:
                self.warning("Could not list directory %r: %s", uri, error)
            else:
                while contents and not has_children:
                    try:
                        filename = contents.next().name
                    except StopIteration:
                        break
                    try:
                        child_uri = uri.join(filename)
                        media_type = self._blocking_get_media_type(child_uri)
                        if media_type:
                            has_children = media_type['file_type'] in media_types
                    except UnicodeDecodeError:
                        ## give a message here?
                        continue
        return has_children


    def _open_directory(self, uri):
        try:
            handle = gnomevfs.open_directory(str(uri))
        except (gnomevfs.NotADirectoryError,
                gnomevfs.ServiceNotAvailableError), exc:
            self.warning("Problem accessing %s : %s" % (uri,exc))
            self.warning("Exception type: %s" % exc.__class__)
            handle = None
        return handle

    def get_media_type(self, uri):
        return self._def_action.enqueue_action(self._blocking_get_media_type, uri)

    def _blocking_get_media_type(self, uri):
        file_type = ''
        mime_type = ''

        if not self._blocking_is_directory(uri):
            mime_type, file_type = misc.get_media_infos_from_mime(uri)
            # TODO: if nothing useful found, use the metadata_manager?
            #file_type = 'audio'
        else:
            file_type = 'directory'

        return { 'file_type' : file_type,
                 'mime_type' : mime_type }

    def get_direct_children(self, uri, children_with_info):
        return self._def_action.enqueue_action(self._blocking_get_direct_children, uri, children_with_info)

    def _blocking_get_direct_children(self, uri, children_with_info):
        # FIXME: Got to check the children first
        # maybe we are loading them again...
        if self._blocking_is_directory(uri):
            handle = self._open_directory(uri)
            index = 0
            if handle:
                for i in handle:
                    if self.hidden_file_pattern.match(i.name):
                        continue
                    #file_type = self.get_file_type(child_uri)
                    try:
                        child = self._uri_join(uri, unicode(i.name))
                        #child = uri.join(unicode(i.name))
                        children_with_info.append( (child, {}) )
                        index += 1
                    except UnicodeDecodeError, e:
                        self.warning("UnicodeDecodeError with filename %s/%s" % (uri, i.name))

        return children_with_info

    def open(self, uri, mode='r'):
        return self._def_action.enqueue_action(self._blocking_open, uri, mode)

    def _blocking_open(self, uri, mode='r'):
        gnome_mode = gnomevfs.OPEN_READ
        if mode == "w":
            gnome_mode = gnomevfs.OPEN_WRITE
        # The other are currently not supported
        ret = None
        if not self._blocking_is_directory(uri):
            try:
                handle = gnomevfs.Handle(str(uri))
            except Exception, exc:
                # Currently I don't know what Exceptions might be raised by
                # gnomevfs.Handle
                self.info("Could not open %s : %s" % (uri, exc))
                # FIXME: should raise it so that caller gets warned
            else:
                ret = MediaFile(self, handle)
        return ret

    def clean(self):
        self._def_action.stop()

    def next_location(self, uri, root=None):
        return self._def_action.enqueue_action(self._blocking_next_location,
                                              uri,root=root)

    def read(self, media_file, size=-1):
        return self._def_action.enqueue_action(self._blocking_read,
                                              media_file, size=size)

    def _blocking_next_location(self, uri, root=None):
        file = uri.filename
        parent = uri.parent
        children = self._blocking_get_direct_children(uri.parent,[])

        files = [ uri.filename for (uri, metadata) in children ]
        files.sort()

        try:
            index = files.index(file) +1
        except ValueError:
            index = 0

        try:
            filename = files[index]
            next_uri = parent + filename
        except IndexError:
            next_uri = None

        return next_uri

    def _blocking_previous_location(self, uri):
        file = uri.filename
        parent = uri.parent
        children = []
        self._blocking_get_direct_children(uri.parent,children)

        files = [ uri.filename for uri in children ]
        files.sort()

        try:
            index = files.index(file) -1
        except ValueError:
            index = 0

        try:
            filename = files[index]
            next_uri = parent + '/' + filename
        except IndexError:
            next_uri = None

        return next_uri

    def _blocking_read(self, media_file, size=-1):
        handle = media_file.descriptor
        data = ""
        if size == -1:
            buffer_size = 1000
            while True:
                try:
                    data_chunk = handle.read(buffer_size)
                    data += data_chunk
                except gnomevfs.EOFError, error:
                    break
        else:
            try:
                data = handle.read(size)
            except gnomevfs.EOFError, error:
                # FIXME: raise exception ?
                self.warning(error)
        return data

