# -*- 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.
#
# Author: Gernot Klimscha <gernot@fluendo.com>

"""
SmbWin32ResourceProvider
"""

from elisa.core.utils import defer
from elisa.core.components.resource_provider import ResourceProvider
from elisa.core.media_uri import MediaUri
from elisa.core.common import application
# Models
from elisa.plugins.base.models.network import NetworkServiceModel
from elisa.plugins.smbwin32.models import ServicesContainer
# Messages
from elisa.plugins.base.messages.network import NetworkServiceAppearedMessage
from elisa.plugins.base.messages.network import NetworkServiceDisappearedMessage

from twisted.internet import threads

# win32api
import win32net, win32netcon, win32wnet

class SmbWin32Resource(ResourceProvider):

    supported_uri='^smb://'

    config_doc = {'search_all_domains' :  'set to 1 to search in all workgroups on the network',
                  'search_domain': 'set to the domain where you want to search for shares,' \
                                   'or leave empty to search in your current domain'
                  }

    default_config = {'search_all_domains': '0',
                      'search_domain': ''
                      }

    def __init__(self):
        super(SmbWin32Resource, self).__init__()

    def initialize(self):
        dfr = super(SmbWin32Resource, self).initialize()
        self._shares = {}
        self._all_domains = int(self.config.get('search_all_domains',''))
        if self._all_domains:
            self._domain = None
        else:
            self._domain = self.config.get('search_domain', '')
            if self._domain == '':
                # get current domain
                self._domain = win32net.NetWkstaGetInfo(None, 100)["langroup"].encode('utf8')
        self.debug("search domain: '%s'" % self._domain)
        self.search_dfr = threads.deferToThread(self._getnetres, self._domain)

        return dfr

    def get(self, uri, model):
        """
        Simple method to retrieve smb shares. You can access it with C{smb://} to
        get all servers that have shares or with C{smb://servername} to get all shares
        of the specified server.

        In return you get a *new* L{elisa.plugins.smbwin32.models.ServicesContainer}
        """

        model = ServicesContainer()

        if uri.host:
            # get all shares from server server_name key in self._shares
            for share_uri in self._shares[str(uri.host)]:
                net_share = NetworkServiceModel()
                net_share.name = share_uri.path.split('/')[-1]
                net_share.type = "file"
                net_share.elisa_uri = share_uri
                model.servers.append(net_share)
        else:
            # get all servers from self._shares
            for server in self._shares.keys():
                net_server = NetworkServiceModel()
                net_server.name = server
                net_server.type = "smb"
                net_server.elisa_uri = MediaUri("smb://" + server)
                model.servers.append(net_server)

        return model, defer.succeed(model)

    def _getnetres(self, domain):
        netres = None
        if domain:
            netres = win32wnet.NETRESOURCE()
            netres.dwScope = win32netcon.RESOURCE_GLOBALNET
            netres.dwType = win32netcon.RESOURCETYPE_DISK
            netres.dwDisplayType = win32netcon.RESOURCEDISPLAYTYPE_GENERIC
            netres.dwUsage = win32netcon.RESOURCEUSAGE_CONNECTABLE
            netres.lpRemoteName = domain

        handle = win32wnet.WNetOpenEnum(win32netcon.RESOURCE_GLOBALNET,
                                        win32netcon.RESOURCETYPE_DISK, 0, netres)
        try:
            shares = self._enumHandle(handle, None)
        finally:
            handle.Close()

        self.debug("yuhu - finished searching for shares!")

    def _enumHandle(self, handle, server):
        enumerating = True
        while enumerating:
            items = win32wnet.WNetEnumResource(handle, 0)
            first_time = True
            for item in items:
                try:
                    if item.dwDisplayType == win32netcon.RESOURCEDISPLAYTYPE_SHARE:
                        # found a share, now append to dict with the server name as key
                        uri = 'file://' + item.lpRemoteName
                        if uri.find('\\') != -1:
                            uri = uri.replace('\\', '/')
                        self.debug("Found share with name: %s" % (item.lpRemoteName))
                        uri = MediaUri(uri)
                        # XXX: if the searching is done regularly than it should be checked before appending the uri if it
                        # isn't already in the dictionary and the shares which aren't there anymore should be removed from the dict
                        self._shares.setdefault(server, []).append(uri)
                        if first_time:
                            first_time = False
                            msg = NetworkServiceAppearedMessage()
                            net_server = NetworkServiceModel()
                            net_server.name = server
                            net_server.type = "smb"
                            net_server.elisa_uri = MediaUri("smb://" + server)
                            msg.model = net_server
                            application.bus.send_message(msg, self)
                    elif item.dwDisplayType == win32netcon.RESOURCEDISPLAYTYPE_GENERIC:
                        self.debug("Found this other resource: '%s'" % (item.lpRemoteName))
                    else:
                        # found a network resource, recursivly search for shares
                        self.debug("Searching for shares in %s ..." % (item.lpRemoteName))
                        # XXX: this is a *very* blocking call
                        k = win32wnet.WNetOpenEnum(win32netcon.RESOURCE_GLOBALNET,
                                                   win32netcon.RESOURCETYPE_DISK, 0, item)
                        self._enumHandle(k, item.lpRemoteName[2:])
                        win32wnet.WNetCloseEnum(k)
                except win32wnet.error, details:
                    self.debug("Couldn't search this resource: %s" % (details[2]))
            else:
                enumerating = False
