"""UserProfile extension for the Django Auth User system.

The UserProfile is edited as part of the base User interface.
"""
from django.db import models
from django.conf import settings
from django.core.cache.backends.locmem import CacheClass as LocalMemCache
from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.utils.html import escape
from pycon.core import *

USERMGR_LOCAL_CACHE_PARAMS = getattr(settings, 'USERMGR_LOCAL_CACHE_PARAMS',
                                     {'cull_frequency': 4,
                                      'max_entries': 3000,
                                     'timeout': 60*60*24*3, # 3 days
                                     })

if getattr(settings, 'USERMGR_USE_LOCAL_CACHE', True):
    cache = LocalMemCache('localhost', USERMGR_LOCAL_CACHE_PARAMS)

############################################################################
##
## Constants
##

## RED_FLAG: turn these into tables and foreign relations (pun intended)
HONORIFIC = Choices([
    ('', ''),
    ('Mr.', 'Mr.'),
    ('Ms.', 'Ms.'),
    ('Mrs.', 'Mrs.'),
    ('Miss', 'Miss'),
    ('Dr.', 'Dr.'),
])

SUFFIX = Choices([
    ('', ''),
    ('Sr.', 'Sr.'),
    ('Jr.', 'Jr.'),
    ('KBE', 'KBE'),
    ('Esq.', 'Esq.'),
    ('I', 'I'),
    ('II', 'II'),
    ('III', 'III'),
    ('IV', 'IV'),
    ('V', 'V'),
    ('1st', '1st'),
    ('2nd', '2nd'),
    ('3rd', '3rd'),
    ('4th', '4th'),
    ('5th', '5th'),
])


@bind_prop_to_class(User, 'html_name')
def __html_name(user):
    cache_name = 'user_html_' + repr(user.id)
    res = cache.get(cache_name)
    if res is not None: return res
    prof = None
    try:
        prof = user.get_profile()
    except:
        pass
    if prof:
        res = prof.html_name
    else:
        # slugs are html safe
        res = user.username
    cache.set(cache_name, res, 24*60*60)
    return res

@bind_meth_to_class(User, '__unicode__')
def __extended_user_unicode(user):
    cache_name = 'user_str_' + repr(user.id)
    res = cache.get(cache_name)
    if res is not None: return res
    prof = None
    try:
        prof = user.get_profile()
    except:
        pass
    if not prof:
        return user.username
    res = prof.long_name
    cache.set(cache_name, res, 24*60*60)
    return res

def uid2pid(uid):
    strid = ""
    while uid:
        strid+="%.2d" %((uid%10) * 9 + 3)
        uid /= 10
    pid = hex(int(strid, 10))[2:]
    if len(pid)%2: pid = '0' + pid
    return pid

def pid2uid(pid, do_except=True):
    try:
        if len(pid)%2: raise ValueError, "Invalid key"
        sid = str(int(pid, 16))
        if len(sid)%2: sid = '0' + sid
    except ValueError:
        if do_except: raise ValueError, "Invalid key"
        return None
    def digits():
        for i in xrange(len(sid)-2, -1, -2):
            x = (int(sid[i:i+2], 10)-3)
            if x%9: raise ValueError, "Invalid key"
            y = str(x/9)
            if len(y) != 1: raise ValueError, "Invalid key"
            yield y
    try:
        uid = int(''.join(digits()), 10)
        return uid
    except ValueError:
        if do_except: raise
        return None
    return None

@bind_prop_to_class(User, 'publicid')
def _gen_public_id(user):
    return uid2pid(user.id)

@bind_meth_to_class(type(User.objects))
def get_for_public_id(manager, pid, do_except=True):
    id = pid2uid(pid)
    try:
        return manager.get_query_set().get(pk=id)
    except User.DoesNotExist:
        if do_except: raise ValueError, "Invalid key"
        return None

@bind_meth_to_class(User)
def get_absolute_url(user):
    return settings.PROFILE_URL + user.publicid + '/'

class UserProfile(models.Model):
    hack = models.BooleanField("Enable Profile", default=True, core=True)
    user = models.ForeignKey(User, unique=True, edit_inline=models.STACKED,
                             num_in_admin=1,min_num_in_admin=1,
                             max_num_in_admin=1,num_extra_on_change=0)
    honorific = models.CharField(maxlength=10, blank=True, null=True,
                                 choices=HONORIFIC,
                                 default=HONORIFIC.default)
    middle = models.CharField(maxlength=30, blank=True, null=True)
    suffix = models.CharField(maxlength=10, blank=True, null=True,
                              choices=SUFFIX,
                              default=SUFFIX.default)
    url = models.URLField(blank=True, null=True)
    affiliation = models.CharField(maxlength=200, blank=True, null=True)
    bio = models.TextField(blank=True)
    code = models.CharField(maxlength=40, editable=False,
                            null=True, blank=True)

    def __init__(self, *args, **kwdargs):
        super(UserProfile, self).__init__(*args, **kwdargs)
        self.hack = True

    def activate(self, code):
        if self.code.upper() == code.upper():
            self.code = ''
            self.user.is_active=True
            self.user.save()
            self.save()
            return True
        return False

    def save(self):
        res = super(UserProfile, self).save()
        cache.delete('user_str_' + repr(self.user_id))
        cache.delete('user_html_' + repr(self.user_id))
        return res

    @property
    def html_name(self):
        base = escape(self.long_name)
        url_fmt = r'%s'
        if self.url:
            url_fmt = '<a href="' + self.url + '">%s</a>'
        if self.affiliation:
            res = base + " (" + url_fmt%escape(self.affiliation) + ")"
        else:
            res = url_fmt % base
        if self.bio:
            res += (' <sup><a href="' + self.user.get_absolute_url() +
                    '" title="bio">bio</a></sup>')
        return res

    @property
    def long_name(self):
        parts = []
        if self.honorific:
            parts.append(self.honorific)
        user = self.user
        if user.first_name:
            parts.append(user.first_name)
        if self.middle:
            parts.append(self.middle)
        if user.last_name:
            parts.append(user.last_name)
        if self.suffix:
            parts.append(self.suffix)
        name = u" ".join(parts)
        if not name.strip():
            name = user.username
        return unicode(name)

    @property
    def short_name(self):
        return u' '.join([self.user.first_name, self.user.last_name])

    @property
    def last_first(self):
        return u', '.join([self.user.last_name, self.user.first_name])

    def __unicode__(self):
        username = u'(' + self.user.username + u')'
        return u' '.join([self.long_name, username])
