"""
Functionality common to more than one protocol goes here.

Of course, everything should be as generic as possible in this folder.
"""

import util
callsback = util.callsback
Timer = util.Timer
from logging import getLogger; log = getLogger('common'); info = log.info

def setfakeprefs(userprefs):
    'Set a fake prefs dict and defaults dict for use in test applications.'
    from common import profile
    _prefs.update(userprefs)

castables = (int, float, long, unicode)

_prefs = {}
_defaultprefs = {}

def set_active_prefs(prefs, defaults=None):
    '''
    Sets the dictionary pref() will find prefs in.
    '''
    if defaults is None:
        defaults = {}
    global _prefs, _defaultprefs
    _prefs = prefs
    _defaultprefs = defaults

def pref(pref, default=sentinel, type=sentinel):
    '''
    Lookup a pref.
    '''

    global _prefs, _defaultprefs

    if default is sentinel:
        default = _defaultprefs.get(pref, default)

    if default is sentinel:
        val = _prefs[pref]
    else:
        val = _prefs.get(pref, default)

    if type is sentinel or val.__class__ is type:
        return val

    if type in castables:
        try: val = type(val)
        except ValueError:
            if default is sentinel: raise TypeError('pref val was incorrect type: %r' % val)
            else: return default
        else:
            return val

    elif not isinstance(val, type):
        if default is sentinel: raise TypeError('pref val was incorrect type: %r' % val)
        else: return default

    return val

class prefprop(object):
    'Read only class property always returning the current value of "pref"'

    __slots__ = ('pref', 'default', 'type')

    def __init__(self, pref, default = sentinel, type = sentinel):
        self.pref = pref
        self.default = default
        self.type = type

    def __repr__(self):
        try:
            msg = repr(self.pref)

            if self.default is not sentinel:
                msg += ', default=%r' % self.default
            if self.type is not sentinel:
                msg += ', type=%r' % self.type
        except Exception:
            msg = '???'

        return '<prefprop %s>' % msg

    def __get__(self, obj, objtype=None):
        try:
            return profile.prefs[self.pref]
        except KeyError:
            if self.default is sentinel:
                raise
            else:
                return self.default

@callsback
def netcall(callable, callback = None):
    from AsyncoreThread import call_later
    call_later(callable, callback = callback)


class _profile_proxy(object):
    '''
    Unfortunately, we use the DigsbyProfile object as a big bad global object
    to store tons of state. Because of this (questionable) design, import
    dependencies get really circular between the digsbyprofile module and just
    about everything else.

    To avoid these problems, most of the codebase does "from common import
    profile" and uses this object (which forwards getattr lookups) as if it
    were actually the digsbyprofile.profile global.
    '''

    def __getattr__(self, attr):
        import digsbyprofile
        globals()['digsbyprofile'] = digsbyprofile
        object.__setattr__(self, '__getattr__',
                           lambda attr, dp=digsbyprofile: getattr(dp.profile, attr))
        return getattr(digsbyprofile.profile, attr)

    def __setattr__(self, key, val):
        raise NotImplementedError()

    def __call__(self):
        from digsbyprofile import profile
        return profile

    def __nonzero__(self):
        from digsbyprofile import profile
        return profile is not None

profile = _profile_proxy()

def setpref(pref, val):
    from digsbyprofile import profile
    prefs = getattr(profile, 'prefs', {})

    prefs[pref] = val

def setprefif(pref, val):
    from digsbyprofile import profile
    prefs = getattr(profile, 'prefs', {})
    if prefs.get(pref, sentinel) != val:
        setpref(pref, val)
        return True

    return False

def delpref(pref):
    from digsbyprofile import profile
    prefs = getattr(profile, 'prefs', {})

    prefs.pop(pref, None)

def silence_notifications(connection, duration=None):
    info('Silencing notifications from %r', connection)

    connection._notifyquiet = True
    def afterTimeout():
        connection._notifyquiet = False
        info('Unsilencing notifications from %r', connection)

    if duration is None:
        duration = pref('notifications.quiet_time', 5)
    timer = Timer(duration, afterTimeout)
    timer.start()
    return timer

from inspect import getargspec

def bind(actionname):
    assert isinstance(actionname, basestring)

    def wrap(func):
        args = getargspec(func)[0]

        # TODO: use inspect to do this correctly.
        # if the function expects self, give it the window that emitted the event.
        if len(args) == 1 and args[0] == 'self':
            def cb(win, func=func):
                return func(win)
        else:
            def cb(win, func=func):
                return func()

        from gui.input import add_action_callback
        add_action_callback(actionname, cb)
        return func

    return wrap

from .hashacct import HashedAccount
from Protocol import Protocol as protocol
from Protocol import StateMixin
from AsyncSocket import AsyncSocket as socket
from Buddy import Buddy as buddy
from Buddy import write_hashes, get_bname
from Conversation import Conversation
from actions import ActionMeta, action, Action, ActionError
from timeoutsocket import TimeoutSocket, TimeoutSocketOne
from hydrasocket import HydraSocket
from filetransfer import FileTransfer, OutgoingFileTransfer, \
    IncomingFileTransfer, IncomingHTTPFileTransfer

from accountbase import AccountBase, FromNetMixin
from statusmessage import StatusMessage, StatusMessageException, acct_reduce
from UpdateMixin import UpdateMixin
from notifications import fire

import commandline

try:
    from contacts.Contact import ContactCapabilities as caps
except ImportError:
    pass

