import os
import stat
from time import time
from random import randint
from PIL import Image
from StringIO import StringIO

from django import forms
from django.conf import settings
from django.utils.translation import ugettext as _

from widgets import *

FILE_PERMISSIONS = getattr(settings, 'FILEUPLOAD_FILE_PERMISSIONS', stat.S_IRUSR | stat.S_IWUSR | stat.S_IRGRP | stat.S_IROTH) # -rw-r-r--

def generate_media_rel_file_path(folder, filename):
    """ 
    Path to save uploaded file relative to settings.MEDIA_ROOT
    Uses time() and random() to generate unique file name
    """
    file_name, file_extension = os.path.splitext(filename)
    name = unicode(int(time())) + '_' + unicode(randint(1E+4,1E+5)) + file_extension    
    return os.path.join(folder, name)

generate_media_rel_file_path = getattr(settings, 'FILEUPLOAD_PATH_GENERATOR', generate_media_rel_file_path)

class FileUploadForm(forms.Form):
    """
    Base file upload form.
    Permits file and request.user validations
    Saves file to file system
    """
    
    uploaded_file = forms.FileField(label = _('file for upload'), required = True)
    old_path = forms.CharField(label = _('old file path'), required = False)
    
    LIFETIME_DAYS = 3   # Temporary files life time before cleanup
    MAX_FILE_SIZE = 1E+7    # Maximum uploaded file size
    VALIDATE_FILE_EXTENSION = True
    VALID_FILE_EXTENSIONS = ('txt',) # use small letters
    
    PERMIT_ANONYMOUS = False    # Enables anonymous users to upload file via this form
    VALIDATE_STUFF_ROLE = True  # Enables not stuff users to upload file via this form
    
    MEDIA_FOLDER = 'text'   # Path to folder for uploaded files relative to settings.MEDIA_ROOT
    
    ENABLE_STATIC_URLS = False    # Enabled file overwrite. Be carfulle! This is not secure outside admin interface
    
    size_is_too_big = _('File can not be bigger than %s bites')
    wrong_file_extension = _('File has illegal file extension. Possible cases: %s')
    autherization_needed = _('You need to be authorized on this site')
    stuff_role_needed = _('You need staff permissions')
    
    def __init__(self, request, customization = {}, **kwargs):
        "customization are capable to override object parameters"
        for key, value in customization.items():
            print key, value
            setattr(self, key, value)
        self.request = request
        super(FileUploadForm, self).__init__(**kwargs)
    
    def validate_perms(self):
        "checks request.user permissions"
        if self.PERMIT_ANONYMOUS:
            return
         
        if self.request.user.is_anonymous():
            raise forms.ValidationError(self.autherization_needed)
        
        if self.VALIDATE_STUFF_ROLE and not self.request.user.is_staff:
            raise forms.ValidationError(self.stuff_role_needed)
        
            
    def validate_file(self):
        uploaded_file = self.cleaned_data['uploaded_file']

        # checks file size
        if uploaded_file.size > self.MAX_FILE_SIZE:
            raise forms.ValidationError(self.size_is_too_big % self.MAX_FILE_SIZE)
        
        # checks file extension
        if self.VALIDATE_FILE_EXTENSION:
            ext_lower = os.path.splitext(uploaded_file.name)[1].lower()[1:]

            if not ext_lower in self.VALID_FILE_EXTENSIONS:
                raise forms.ValidationError(self.wrong_file_extension % (', '.join(self.VALID_FILE_EXTENSIONS)))

    def clean_uploaded_file(self):
        self.validate_perms()
        self.validate_file()
        
        uploaded_file = self.cleaned_data['uploaded_file']
        return uploaded_file
    
    def create_temp_file_record(self, rel_path):
        "mark file as temporary with help of TempFile model"
        
        from models import TempFile
        TempFile.objects.add_record(rel_path, lifetime_days = self.LIFETIME_DAYS)
    
    def handle_uploaded_file(self):
        "saves file to file system, marks it as temporary, returns relative to MEDIA_ROOT path"
        obj = self.cleaned_data['uploaded_file']
        print self.ENABLE_STATIC_URLS, self.cleaned_data['old_path']
        if self.ENABLE_STATIC_URLS and self.cleaned_data['old_path']:
            rel_path = self.cleaned_data['old_path']
        else:
            rel_path = generate_media_rel_file_path(self.MEDIA_FOLDER, obj.name)

        abs_path = os.path.join(settings.MEDIA_ROOT, rel_path)
        # saves file with permissions
        destination = open(abs_path, 'w')
        for chunk in obj.chunks():
            destination.write(chunk)
        destination.close()
        os.chmod(abs_path, FILE_PERMISSIONS)

        self.create_temp_file_record(rel_path)

        return rel_path

    def flatify_errors(self):
        "returns errors description as html"
        if "__all__" in self.errors:
            return '<br/>'.join(self.errors["__all__"])
        else:
            return '<br/>'.join([''.join(field_errors.as_text()) for field_errors in self.errors.values()])


class ImageUploadForm(FileUploadForm):
    """
    Form for AJAX uploaded image validation
    Contains settings for FileUploadForm, special image preprocessing
    Image validations is performed by Django forms.ImageField
    """
    
    uploaded_file = forms.ImageField(label = _('image for upload'), required = True)

    AUTO_COMPRESS = False # enables compress image preprocessing
    COMPRESS_METHODS = ('scale')
    MAX_IMAGE_SIZE = [500, 500] # scale compress method parameter

    MAX_FILE_SIZE = 5E+6
    VALID_FILE_EXTENSIONS = ('jpg', 'jpeg', 'gif', 'png', 'bmp',)
    MEDIA_FOLDER = 'images'

    
    def scale(self, im, requested_size, to_max=True):
        "resize image"
        print im
        x, y = [float(v) for v in im.size]
        xr, yr = [float(v) for v in requested_size]

        r = min(xr / x, yr / y)

        if r < 1.0:
            im = im.resize((int(x * r), int(y * r)), resample=Image.ANTIALIAS)

        return im
        
    def clean_uploaded_file(self):
        "Specific for image preprocessing, listed in COMPRESS_METHODS "
        image_file = super(ImageUploadForm,self).clean_uploaded_file()

        if self.AUTO_COMPRESS:
            im = Image.open(StringIO(image_file.read()))
            if 'scale' in self.COMPRESS_METHODS:
                print im
                im = self.scale(im, self.MAX_IMAGE_SIZE)
            image_file.seek(0)
            im.save(image_file)
        return image_file

class FileUploadFormField(forms.FileField):
    """
        Form field for FileUploadModelField
        Uses FileUploadWidget
        Performs special clean procedure - crack protection
    """
    widget = FileUploadWidget()
    file_does_not_exist = _('File does not exist')
    exceeded_max_length = _('File path length are exceeded')
    
    def to_python(self, data):
        if data in validators.EMPTY_VALUES:
            return None

        if self.max_length is not None and len(file_name) > self.max_length:
            raise forms.ValidationError(self.exceeded_max_length)

        return data

    def clean(self, data, initial=None):
        if initial==data:
            return initial
        
        if not data:
            return None

        # Checks for temporary file existence - crack protection
        from models import TempFile
        if TempFile.objects.filter(path=data).count()==0:
            raise forms.ValidationError(self.file_does_not_exist)

        return data
