
# Slixmpp: The Slick XMPP Library
# Copyright (C) 2011 Nathanael C. Fritz, Lance J.T. Stout
# This file is part of Slixmpp.
# See the file LICENSE for copying permission.
import logging

from slixmpp.xmlstream import JID
from slixmpp.exceptions import IqError, IqTimeout


log = logging.getLogger(__name__)


class StaticCaps(object):

    """
    Extend the default StaticDisco implementation to provide
    support for extended identity information.
    """

    def __init__(self, xmpp, static):
        """
        Augment the default XEP-0030 static handler object.

        Arguments:
            static -- The default static XEP-0030 handler object.
        """
        self.xmpp = xmpp
        self.disco = self.xmpp['xep_0030']
        self.caps = self.xmpp['xep_0115']
        self.static = static
        self.jid_vers = {}

    async def supports(self, jid, node, ifrom, data):
        """
        Check if a JID supports a given feature.

        The data parameter may provide:
            feature  -- The feature to check for support.
            local    -- If true, then the query is for a JID/node
                        combination handled by this Slixmpp instance and
                        no stanzas need to be sent.
                        Otherwise, a disco stanza must be sent to the
                        remove JID to retrieve the info.
            cached   -- If true, then look for the disco info data from
                        the local cache system. If no results are found,
                        send the query as usual. The self.use_cache
                        setting must be set to true for this option to
                        be useful. If set to false, then the cache will
                        be skipped, even if a result has already been
                        cached. Defaults to false.
        """
        feature = data.get('feature', None)

        data = {'local': data.get('local', False),
                'cached': data.get('cached', True)}

        if not feature:
            return False

        if node in (None, ''):
            info = await self.caps.get_caps(jid)
            if info and feature in info['features']:
                return True

        try:
            info = await self.disco.get_info(jid=jid, node=node,
                                             ifrom=ifrom, **data)
            info = self.disco._wrap(ifrom, jid, info, True)
            return feature in info['disco_info']['features']
        except IqError:
            return False
        except IqTimeout:
            return None

    async def has_identity(self, jid, node, ifrom, data):
        """
        Check if a JID has a given identity.

        The data parameter may provide:
            category -- The category of the identity to check.
            itype    -- The type of the identity to check.
            lang     -- The language of the identity to check.
            local    -- If true, then the query is for a JID/node
                        combination handled by this Slixmpp instance and
                        no stanzas need to be sent.
                        Otherwise, a disco stanza must be sent to the
                        remove JID to retrieve the info.
            cached   -- If true, then look for the disco info data from
                        the local cache system. If no results are found,
                        send the query as usual. The self.use_cache
                        setting must be set to true for this option to
                        be useful. If set to false, then the cache will
                        be skipped, even if a result has already been
                        cached. Defaults to false.
        """
        identity = (data.get('category', None),
                    data.get('itype', None),
                    data.get('lang', None))

        data = {'local': data.get('local', False),
                'cached': data.get('cached', True)}

        trunc = lambda i: (i[0], i[1], i[2])

        if node in (None, ''):
            info = self.caps.get_caps(jid)
            if info and identity in map(trunc, info['identities']):
                return True

        try:
            info = await self.disco.get_info(jid=jid, node=node,
                                             ifrom=ifrom, **data)
            info = self.disco._wrap(ifrom, jid, info, True)
            return identity in map(trunc, info['disco_info']['identities'])
        except IqError:
            return False
        except IqTimeout:
            return None

    def cache_caps(self, jid, node, ifrom, data):
        verstring = data.get('verstring', None)
        info = data.get('info', None)
        if not verstring or not info:
            return
        self.caps.cache.store(verstring, info)

    def assign_verstring(self, jid, node, ifrom, data):
        if isinstance(jid, JID):
            jid = jid.full
        self.jid_vers[jid] = data.get('verstring', None)

    def get_verstring(self, jid, node, ifrom, data):
        return self.jid_vers.get(jid, None)

    async def get_caps(self, jid, node, ifrom, data):
        verstring = data.get('verstring', None)
        if verstring is None:
            return None
        return self.caps.cache.retrieve(verstring)
