import traceback

from util import callsback
import common.actions
action = common.actions.action
ObservableActionMeta = common.actions.ObservableActionMeta
from common import profile
from logging import getLogger; log = getLogger('Contact')

objget = object.__getattribute__

CONTACT_ATTRS = set(['id', 'buddy', 'remove', 'watched', '__repr__',
                     'rename_gui', 'rename', 'edit_alerts', 'alias', 'get_group', 'move_to_group'
                     '__getattr__', '__hash__', '__cmp__', 'sort', '_notify_dirty',
                     ])

class Contact(object):
    '''
    Contact. Represents an entry on a protocol buddy list.

    Several contacts may point to the same Buddy object.
    Ex: AIM when a buddy is in 2+ groups - same buddy in both
    places but each has its own SSI.
    '''

    watched = 'online '.split()
    __metaclass__ = ObservableActionMeta

    def __init__(self, buddy, id):
        self.buddy, self.id = buddy, id
        self._metacontact = None

    def remove(self):
        self.protocol.remove_buddy(self.id)

    def _compatible_accounts(self):
        from common.protocolmeta import is_compatible
        result = []
        for account in profile.connected_accounts:
            if is_compatible(account.protocol, self.buddy.service):
                result.append(account)

        return result

    def _all_buddies(self, check_if_has=False):
        result = []
        for account in self._compatible_accounts():
            connection = account.connection
            if connection:
                if not check_if_has or connection.has_buddy(self.buddy.name):
                    # don't let a protocol create the buddy
                    buddy = connection.get_buddy(self.buddy.name)
                    if buddy is not None:
                        result.append(buddy)

        return result


    def _is_blocked(self):
        buddies = [buddy.blocked for buddy in self._all_buddies(check_if_has=True)]
        return bool(buddies and all(buddies))

    blocked = property(_is_blocked)

    def _block_pred(self, block=True, **k):
        return True if bool(block) ^ self._is_blocked() else None
    def _unblock_pred(self, *a, **k):
        return True if self._is_blocked() else None

    @action(_block_pred)
    def block(self, block=True, **k):
        for buddy in self._all_buddies():
            if bool(block) ^ bool(buddy.blocked):
                buddy.block(block, **k)

    @action(_unblock_pred)
    def unblock(self, *a,**k):
        self.block(False,*a,**k)

    def get_notify_dirty(self):
        return self.buddy._notify_dirty

    def set_notify_dirty(self, value):
        self.buddy._notify_dirty = value

    _notify_dirty = property(get_notify_dirty, set_notify_dirty)

    @action()
    def rename_gui(self):
        from gui.toolbox import GetTextFromUser

        localalias = self.alias
        if localalias is None:
            localalias = ''

        s = GetTextFromUser(_('Enter an alias for %s:') % self.name,
                                           caption = _('Rename %s') % self.name,
                                           default_value = localalias )
        if s is not None:
            if s == '' or s.strip():
                # dialog returns None if "Cancel" button is pressed -- that means do nothing

                # rename expects None to mean "no alias" and anything else to mean an alias--so
                # do the bool check to turn '' into None here.
                self.rename(s if s else None)
                return s

    def rename(self, new_alias):
        log.info('setting alias for %r to %r', self, new_alias)
        profile.set_contact_info(self, 'alias', new_alias)
        self.buddy.notify('alias')

    @action()
    def edit_alerts(self):
        import gui.pref.prefsdialog as prefsdialog
        prefsdialog.show('notifications')

    @property
    def alias(self):
        a = profile.get_contact_info(self, 'alias')
        if a: return a

        for attr in ('local_alias', 'remote_alias', 'nice_name'):
            try:
                a = getattr(self, attr, None)
            except Exception:
                traceback.print_exc()
                continue

            if a: return a

        return self.name

    def get_group(self):
        g = self.protocol.group_for(self)
        assert isinstance(g, (basestring, type(None))), 'Is %s' % type(g)
        return g

    @callsback
    def move_to_group(self, groupname, index = 0, callback = None):
        if not isinstance(groupname, basestring):
            raise TypeError, 'groupname must be a string: %r' % groupname

        self.protocol.move_buddy_creating_group(self, groupname, self.get_group(),
                                                index, callback = callback)

    def __getattr__(self, attr):
        if attr in CONTACT_ATTRS:
            return objget(self, attr)
        else:
            return getattr(objget(self, 'buddy'), attr)

    def __repr__(self):
        return '<%s %s>' % (self.__class__.__name__, self.buddy)

    def __hash__(self):
        # First part of this hash should match Buddy.idstr()
        b = self.buddy
        id = self.id
        if isinstance(id, bytes):
            id = id.decode('fuzzy utf-8')
        return hash(u'/'.join((b.protocol.name, b.protocol.username, b.name, unicode(id))))

    def __cmp__(self, other):
        if self is other:
            return 0
        else:
            return cmp((self.buddy, self.id), (getattr(other, 'buddy', None), getattr(other, 'id', None)))

class ContactCapabilities:
    'Buddy capabilities. Exposed as common.caps'

    INFO          = 'INFO'

    IM            = 'IM'
    'Instant messaging.'

    FILES         = 'FILES'
    'Sending and receiving files.'

    PICTURES      = 'PICTURES'
    'Sharing pictures over a direct connection.'

    SMS           = 'SMS'
    'Sending messages directly to a cell phone.'

    BLOCKABLE     = 'BLOCKABLE'
    'Blocking buddies.'

    EMAIL         = 'EMAIL'
    'Sending email.'

    BOT = 'BOT'
    'User is a bot, and will join the Machines when Skynet turns on the human race. Be vigilant.'

    VIDEO = 'VIDEO'
    'Video chat.'
