# vim:spell:spelllang=fr
# -*- encoding: utf-8 -*-

import random
import string
import datetime
import hmac
import hashlib

from django.shortcuts import get_object_or_404
from django.conf import settings
from django.contrib.auth import models as auth_models
from django.template import Template, Context
from django.core.mail import EmailMessage
from django.core.cache import cache
from django.core.urlresolvers import reverse
from django.db.models.signals import post_init

import models
from appli_project import app_settings

def random_string(length):
    '''Return a random string'''
    return ''.join((random.choice(string.letters+string.digits) for i in xrange(length)))

def build_random_username(prefix, max_length=30):
    return '{0}#{1}'.format(prefix, random_string(max_length-1-len(prefix)))

def next_url(request, default=None):
        return request.POST.get('next') or \
                request.GET.get('next') or \
                default or \
                request.META.get('HTTP_REFERER') or \
                '/'

def extraire_action_and_model(request, actions, model):
        for key in request.POST:
            if '-' not in key:
                continue
            action, pk = key.split('-')
            if action not in actions:
                continue
            instance = get_object_or_404(model, pk=pk)
            return action, instance
        return None, None

def extra_sauvegardee(qs, user):
    '''Utilise une sous-requête SQL pour savoir si une annonce a été sauvegardée ou pas'''
    qs = qs.extra(
            select={
                'sauvegardee': 'EXISTS (SELECT id FROM appli_socle_profilrecherche_annonces_sauvegardees WHERE appli_socle_profilrecherche_annonces_sauvegardees.profilrecherche_id = %s AND appli_socle_profilrecherche_annonces_sauvegardees.annonce_id = appli_socle_annonce.id)'
            },
            select_params =[user.id])
    return qs

def mail_modele(nom, sujet_par_defaut, texte_par_defaut):
    modele, created = models.EmailModele.objects.get_or_create(nom=nom)
    if created:
        modele.sujet = unicode(sujet_par_defaut)
        modele.texte = unicode(texte_par_defaut)
        modele.save()
    return modele

def mail_avec_modele(nom, sujet_par_defaut, texte_par_defaut, variables, _from,
        to, headers=[]):
    modele = mail_modele(nom, sujet_par_defaut, texte_par_defaut)
    ctx = Context(variables)
    sujet = Template(modele.sujet).render(ctx)
    texte = Template(modele.texte).render(ctx)
    if isinstance(to, basestring):
        to = [to]
    email = EmailMessage(sujet, texte, _from, to, headers=headers)
    email.send()

def mail_avec_modele_par_defaut(nom, variables, _from, to, headers=[]):
    defaut = app_settings.MAIL_TEMPLATES[nom]
    return mail_avec_modele(nom, defaut['sujet'], defaut['texte'], variables,
            _from, to, headers=headers)

JETON_CHARS = '123456789ABCDEFGHJKMNPQRSTUVWXYZ'

def envoyer_jeton(request, nom, sujet_par_defaut, texte_par_defaut, variables, _from,
        to, contenu_du_jeton, headers=[]):
        jeton = ''.join([random.SystemRandom().choice(JETON_CHARS)
            for i in range(8)])
        jeton_url = request.build_absolute_uri(
                reverse('confirmation-email', kwargs=dict(jeton=jeton)))
        confirmation_url = request.build_absolute_uri(
                reverse('confirmation-email', kwargs=dict(jeton='')))
        host = request.get_host()
        variables = variables.copy()
        variables.update(dict(jeton_url=jeton_url,
            confirmation_url=confirmation_url, host=host, jeton=jeton))
        contenu_du_jeton = contenu_du_jeton.copy()
        cache.set('inscription_%s' % jeton, contenu_du_jeton)
        mail_avec_modele(nom, sujet_par_defaut, texte_par_defaut, variables,
                _from, to)
        return jeton

def track_data(*fields):
    """
    Tracks property changes on a model instance.
    
    The changed list of properties is refreshed on model initialization
    and save.
    
    >>> @track_data('name')
    >>> class Post(models.Model):
    >>>     name = models.CharField(...)
    >>> 
    >>>     @classmethod
    >>>     def post_save(cls, sender, instance, created, **kwargs):
    >>>         if instance.has_changed('name'):
    >>>             print "Hooray!"
    """
    
    UNSAVED = dict()

    def _store(self):
        "Updates a local copy of attributes values"
        if self.id:
            self.__data = dict((f, getattr(self, f)) for f in fields)
        else:
            self.__data = UNSAVED

    def inner(cls):
        # contains a local copy of the previous values of attributes
        cls.__data = {}

        def has_changed(self, field):
            "Returns ``True`` if ``field`` has changed since initialization."
            if self.__data is UNSAVED:
                return False
            return self.__data.get(field) != getattr(self, field)
        cls.has_changed = has_changed

        def old_value(self, field):
            "Returns the previous value of ``field``"
            return self.__data.get(field)
        cls.old_value = old_value

        def whats_changed(self):
            "Returns a list of changed attributes."
            changed = {}
            if self.__data is UNSAVED:
                return changed
            for k, v in self.__data.iteritems():
                if v != getattr(self, k):
                    changed[k] = v
            return changed
        cls.whats_changed = whats_changed

        # Ensure we are updating local attributes on model init
        def _post_init(sender, instance, **kwargs):
            _store(instance)
        post_init.connect(_post_init, sender=cls, weak=False)

        # Ensure we are updating local attributes on model save
        def save(self, *args, **kwargs):
            save._original(self, *args, **kwargs)
            _store(self)
        save._original = cls.save
        cls.save = save
        return cls
    return inner

def iter_slice(slicable, length):
    i = 0
    while True:
        l = slicable[i:i+length]
        yield l
        if len(l) < length:
            break
        i += length
