from path import path
from PIL import Image
from zipfile import is_zipfile
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
from django.shortcuts import redirect
from django.utils.encoding import smart_str
from django.utils.translation import ugettext as _
from misago.apps.errors import error404
from misago.decorators import block_guest
from misago.forms import FormLayout
from misago.messages import Message
from misago.utils.strings import random_string
from misago.utils.avatars import resizeimage
from misago.apps.usercp.template import RequestContext
from misago.apps.usercp.avatar.forms import UploadAvatarForm

def avatar_view(f):
    def decorator(*args, **kwargs):
        request = args[0]
        if request.user.avatar_ban:
            return request.theme.render_to_response('usercp/avatar_banned.html',
                                                    context_instance=RequestContext(request, {
                                                     'tab': 'avatar',
                                                     }));
        return f(*args, **kwargs)
    return decorator


@block_guest
@avatar_view
def avatar(request):
    message = request.messages.get_message('usercp_avatar')
    return request.theme.render_to_response('usercp/avatar.html',
                                            context_instance=RequestContext(request, {
                                             'message': message,
                                             'tab': 'avatar',
                                             }));


@block_guest
@avatar_view
def gravatar(request):
    if not 'gravatar' in request.settings.avatars_types:
        return error404(request)
    if request.user.avatar_type != 'gravatar':
        if request.csrf.request_secure(request):
            request.user.delete_avatar()
            request.user.avatar_type = 'gravatar'
            request.user.save(force_update=True)
            request.messages.set_flash(Message(_("Your avatar has been changed to Gravatar.")), 'success', 'usercp_avatar')
        else:
            request.messages.set_flash(Message(_("Request authorisation is invalid.")), 'error', 'usercp_avatar')
    return redirect(reverse('usercp_avatar'))


@block_guest
@avatar_view
def gallery(request):
    if not 'gallery' in request.settings.avatars_types:
        return error404(request)

    allowed_avatars = []
    galleries = []
    for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
        if directory[-7:] != '_locked' and directory[-8:] != '_default':
            gallery = {'name': directory[-7:], 'avatars': []}
            avatars = directory.files('*.gif')
            avatars += directory.files('*.jpg')
            avatars += directory.files('*.jpeg')
            avatars += directory.files('*.png')
            for item in avatars:
                gallery['avatars'].append('/'.join(path(item).splitall()[-2:]))
            galleries.append(gallery)
            allowed_avatars += gallery['avatars']

    if not allowed_avatars:
        request.messages.set_flash(Message(_("No avatar galleries are available at the moment.")), 'info', 'usercp_avatar')
        return redirect(reverse('usercp_avatar'))

    message = request.messages.get_message('usercp_avatar')
    if request.method == 'POST':
        if request.csrf.request_secure(request):
            new_avatar = request.POST.get('avatar_image')
            if new_avatar in allowed_avatars:
                request.user.delete_avatar()
                request.user.avatar_type = 'gallery'
                request.user.avatar_image = new_avatar
                request.user.save(force_update=True)
                request.messages.set_flash(Message(_("Your avatar has been changed to one from gallery.")), 'success', 'usercp_avatar')
                return redirect(reverse('usercp_avatar'))
            message = Message(_("Selected Avatar is incorrect."), 'error')
        else:
            message = Message(_("Request authorisation is invalid."), 'error')

    return request.theme.render_to_response('usercp/avatar_gallery.html',
                                            context_instance=RequestContext(request, {
                                             'message': message,
                                             'galleries': galleries,
                                             'tab': 'avatar',
                                             }));


@block_guest
@avatar_view
def upload(request):
    if not 'upload' in request.settings.avatars_types:
        return error404(request)
    message = request.messages.get_message('usercp_avatar')
    if request.method == 'POST':
        form = UploadAvatarForm(request.POST, request.FILES, request=request)
        if form.is_valid():
            request.user.delete_avatar_temp()
            image = form.cleaned_data['avatar_upload']
            image_name, image_extension = path(smart_str(image.name.lower())).splitext()
            image_name = '%s_tmp_%s%s' % (request.user.pk, random_string(8), image_extension)
            image_path = settings.MEDIA_ROOT + 'avatars/' + image_name
            request.user.avatar_temp = image_name

            with open(image_path, 'wb+') as destination:
                for chunk in image.chunks():
                    destination.write(chunk)
            request.user.save()
            try:
                if is_zipfile(image_path):
                    # Composite file upload
                    raise ValidationError()                 
                image = Image.open(image_path)
                if not image.format in ['GIF', 'PNG', 'JPEG']:
                    raise ValidationError()
                image.seek(0)
                image.save(image_path)
                if request.POST.get('js_check'):
                    return redirect(reverse('usercp_avatar_upload_crop'))
                # Redirect to crop page didnt happen, handle avatar with old school hollywood way
                image_path = settings.MEDIA_ROOT + 'avatars/'
                source = Image.open(image_path + request.user.avatar_temp)
                image_name, image_extension = path(request.user.avatar_temp).splitext()
                image_name = '%s_%s%s' % (request.user.pk, random_string(8), image_extension)
                resizeimage(source, settings.AVATAR_SIZES[0], image_path + image_name, info=source.info, format=source.format)
                for size in settings.AVATAR_SIZES[1:]:
                    resizeimage(source, size, image_path + str(size) + '_' + image_name, info=source.info, format=source.format)
                # Update user model one more time
                request.user.delete_avatar_image()
                request.user.delete_avatar_original()
                request.user.avatar_type = 'upload'
                request.user.avatar_original = '%s_org_%s%s' % (request.user.pk, random_string(8), image_extension)
                source.save(image_path + request.user.avatar_original)
                request.user.delete_avatar_temp()
                request.user.avatar_image = image_name
                request.user.save(force_update=True)
                # Set message and adios!
                request.messages.set_flash(Message(_("Your avatar has changed.")), 'success', 'usercp_avatar')
                return redirect(reverse('usercp_avatar'))
            except ValidationError:
                request.user.delete_avatar()
                request.user.default_avatar(request.settings)
                message = Message(_("Only gif, jpeg and png files are allowed for member avatars."), 'error')
        else:
            message = Message(form.non_field_errors()[0], 'error')
    else:
        form = UploadAvatarForm(request=request)

    return request.theme.render_to_response('usercp/avatar_upload.html',
                                            context_instance=RequestContext(request, {
                                             'message': message,
                                             'form': FormLayout(form),
                                             'tab': 'avatar',
                                             }));


@block_guest
@avatar_view
def crop(request, upload=False):
    if upload and (not request.user.avatar_temp or not 'upload' in request.settings.avatars_types):
        return error404(request)

    if not upload and request.user.avatar_type != 'upload':
        request.messages.set_flash(Message(_("Crop Avatar option is avaiable only when you use uploaded image as your avatar.")), 'error', 'usercp_avatar')
        return redirect(reverse('usercp_avatar'))

    message = request.messages.get_message('usercp_avatar')
    if request.method == 'POST':
        if request.csrf.request_secure(request):
            try:
                image_path = settings.MEDIA_ROOT + 'avatars/'
                if upload:
                    source = Image.open(image_path + request.user.avatar_temp)
                else:
                    source = Image.open(image_path + request.user.avatar_original)
                width, height = source.size

                aspect = float(width) / float(request.POST['crop_b'])
                crop_x = int(aspect * float(request.POST['crop_x']))
                crop_y = int(aspect * float(request.POST['crop_y']))
                crop_w = int(aspect * float(request.POST['crop_w']))
                crop = source.crop((crop_x, crop_y, crop_x + crop_w, crop_y + crop_w))

                if upload:
                    image_name, image_extension = path(request.user.avatar_temp).splitext()
                else:
                    image_name, image_extension = path(request.user.avatar_original).splitext()
                image_name = '%s_%s%s' % (request.user.pk, random_string(8), image_extension)
                resizeimage(crop, settings.AVATAR_SIZES[0], image_path + image_name, info=source.info, format=source.format)
                for size in settings.AVATAR_SIZES[1:]:
                    resizeimage(crop, size, image_path + str(size) + '_' + image_name, info=source.info, format=source.format)

                request.user.delete_avatar_image()
                if upload:
                    request.user.delete_avatar_original()
                    request.user.avatar_type = 'upload'
                    request.user.avatar_original = '%s_org_%s%s' % (request.user.pk, random_string(8), image_extension)
                    source.save(image_path + request.user.avatar_original)
                request.user.delete_avatar_temp()
                request.user.avatar_image = image_name
                request.user.save(force_update=True)
                request.messages.set_flash(Message(_("Your avatar has been cropped.")), 'success', 'usercp_avatar')
                return redirect(reverse('usercp_avatar'))
            except Exception:
                message = Message(_("Form contains errors."), 'error')
        else:
            message = Message(_("Request authorisation is invalid."), 'error')


    return request.theme.render_to_response('usercp/avatar_crop.html',
                                            context_instance=RequestContext(request, {
                                             'message': message,
                                             'after_upload': upload,
                                             'avatar_size': settings.AVATAR_SIZES[0],
                                             'source': 'avatars/%s' % (request.user.avatar_temp if upload else request.user.avatar_original),
                                             'tab': 'avatar',
                                             }));
