from datetime import datetime, timedelta

from django.db import models
from django.conf import settings
from django.utils.translation import ugettext as _

from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

from forms import FileUploadFormField
from widgets import FileUploadWidget, ImageUploadWidget

class TempFileManager(models.Manager):
    def add_record(self, path, lifetime_days = 0, lifetime_hours = 0, lifetime_mins =0, lifetime_secs = 0):
        delta = timedelta(days = lifetime_days, hours = lifetime_hours, minutes = lifetime_mins, seconds = lifetime_secs)
        kwargs = {'path': path, 'delete_after': datetime.now() + delta}

        f=self.create(**kwargs)
        f.save()
        return f

class TempFile(models.Model):
    """
    Record about temporary file. It is used to remember temporary loaded and not approved files.
    To clean records use "./manage.py clean_temp_files" command
    """
    
    path = models.CharField(_('path'), max_length = 200, null=False, blank=False, help_text=_('path relative to value of MEDIA_ROOT'))

    created = models.DateTimeField(verbose_name=_('created'), auto_now_add = True)
    delete_after = models.DateTimeField(verbose_name=_('delete after'), null = False, blank = False)
    
    class Meta:
        verbose_name = _('temporary file')
        verbose_name_plural = _('temporary files')
        ordering = ('created', )

    objects = TempFileManager()
    
    def __unicode__(self):
        return settings.MEDIA_ROOT + self.path
        
class FileUploadModelField(models.CharField):
    """
    Model field for AJAX file upload.
    Works like simple CharField with some default settings and FileUploadFormField as default form field
    Differs from FileField: know nothing about upload_to, can zeroize its value
    Default widget can be specified directly in the __init__
    """
    
    FORM_CLASS = FileUploadFormField
    
    def __init__(self, verbose_name=None, widget=None, **kwargs):
        "sets default max_length"
        self.widget = widget
        kwargs.setdefault('max_length', 100)
        super(FileUploadModelField, self).__init__(verbose_name, **kwargs)
        
    def formfield(self, **kwargs):
        "sets default form class and widget"
        defaults = {
            'form_class': self.FORM_CLASS,
         }
        defaults.update(kwargs)
        defaults['widget'] = self.widget
        
        return super(FileUploadModelField, self).formfield(**defaults)

# south application model field registration
try:
    from south.modelsinspector import add_introspection_rules
except ImportError:
    pass
else:
    add_introspection_rules([
       (
           [FileUploadModelField], # Class(es) these apply to
           [],         # Positional arguments (not used)
           {},         # Keyword argument
       ),
    ], ["^fileupload\.models\.FileUploadModelField"])
        
class FilesHolderModel(models.Model):
    "Base class - container for FileUploadModelField model field"

    def update(self, commit=True):
        """
        Updates all files, assigned to current object (searches FileUploadModelField model fields)
        and comparing with old record in the database (additional db query is used)
        
        Old files will be marked as temporary with help of TempFile model
        New files will be unmarked
        
        commit - if we need to save current object to database

        Works not fast enought. The best use case - call method during form processing
        """
        cl = type(self)
        fields = [field for field in self._meta.fields if isinstance(field, FileUploadModelField)]
        
        try:
            old_obj = cl.objects.get(id = self.id)
        except cl.DoesNotExist:
            for field in fields:
                new_value = getattr(self, field.name)
                if new_value:
                    try:
                        TempFile.objects.filter(path = new_value).delete()
                    except TempFile.DoesNotExist:
                        pass
        else:
            for field in fields:
                old_value = getattr(old_obj, field.name)
                new_value = getattr(self, field.name)
                if not old_value==new_value:
                    if old_value:
                        TempFile.objects.add_record(path = old_value)
                    if new_value:
                        try:
                            TempFile.objects.filter(path = new_value).delete()
                        except TempFile.DoesNotExist:
                            pass
        if commit:
            self.save()

    def delete(self, using=None): 
        """
        Deletes all files, assigned to this object( searches FileUploadModelField model fields)
        """
        super(FilesHolderModel, self).delete(using)
        for field in self._meta.fields:
            if isinstance(field, FileUploadModelField):
                value = getattr(self, field.name)
                if value:
                    TempFile.objects.add_record(path = value)
    
    class Meta:
        abstract = True

class AttachedFile(FilesHolderModel):
    """
    File attached to some model object
    Checks its file in each save() method call beacause of problems with admin inline forms
    """
    content_type = models.ForeignKey(ContentType, related_name='attached_file_content_type')
    object_id = models.PositiveIntegerField()
    owner = generic.GenericForeignKey('content_type', 'object_id')
    
    file = FileUploadModelField(_('file'), widget=FileUploadWidget(attrs={
        'ALLOW_CHANGE': False,
        'ALLOW_DELETE': False,
    }) )

    created = models.DateTimeField(verbose_name=_('created'), auto_now_add = True )

    class Meta:
        verbose_name = _('file')
        verbose_name_plural = _('files')
        ordering = ('-created', )

    def save(self):
        self.update(commit=False)
        super(AttachedFile, self).save()
    
class AttachedImage(FilesHolderModel):
    """
    Image attached to some model object
    Checks its file in each save() method call beacause of problems with admin inline forms
    """
    
    content_type = models.ForeignKey(ContentType, related_name='attached_image_content_type')
    object_id = models.PositiveIntegerField()
    owner = generic.GenericForeignKey('content_type', 'object_id')
    
    image = FileUploadModelField(_('image'), widget=ImageUploadWidget(attrs = {
        'ALLOW_CHANGE': False,
        'ALLOW_DELETE': False,
    }))

    created = models.DateTimeField(verbose_name=_('created'), auto_now_add = True )

    class Meta:
        verbose_name = _('image')
        verbose_name_plural = _('images')
        ordering = ('-created', )

    def save(self):
        self.update(commit=False)
        super(AttachedImage, self).save()

class AttachedOrderedImage(AttachedImage):
    
    order = models.PositiveIntegerField(_('order'), default=0)
    
    class Meta:
        verbose_name = _('ordered image')
        verbose_name_plural = _('ordered images')
        ordering = ('-created', )
