from django.core.files.storage import FileSystemStorage
from django.core.files.storage import Storage
from django.conf import settings
from django.core.files import locks, File

import hashlib
import os
import errno

# A handy base-conversion thingie
from base_n_utils import base_n_encode

class OverwritingStorage(FileSystemStorage):
    """
    FileSystemStorage that allows overwriting files
    """
    def get_available_name(self, name):
        return name

    def _save(self, name, content):
        """
        lifted from django/core/files/storage.py
        """
        full_path = self.path(name)

        directory = os.path.dirname(full_path)
        if not os.path.exists(directory):
            os.makedirs(directory)
        elif not os.path.isdir(directory):
            raise IOError("%s exists and is not a directory." % directory)

        # This file has a file path that we can move.
        if hasattr(content, 'temporary_file_path'):
            file_move_safe(content.temporary_file_path(), full_path)
            content.close()
            
        # This is a normal uploadedfile that we can stream.
        else:
            # This fun binary flag incantation makes os.open throw an
            # OSError if the file already exists before we open it.
            try:
                fd = os.open(full_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL | getattr(os, 'O_BINARY', 0))
                locks.lock(fd, locks.LOCK_EX)
                for chunk in content.chunks():
                    os.write(fd, chunk)
                locks.unlock(fd)
                os.close(fd)
            except OSError, e:
                if e.errno != errno.EEXIST:
                    raise
                else:
                    # TODO: Should check if files are identical and shout if they are not
                    pass

        if settings.FILE_UPLOAD_PERMISSIONS is not None:
            os.chmod(full_path, settings.FILE_UPLOAD_PERMISSIONS)

        return name

    def delete(self, name):
        """
        Never deletes anything

        TODO: Could conceivably check if any ImageFields in the application are pointing to it
        """
        pass

    @classmethod
    def hashed_path(cls, image):
        m = hashlib.md5()
        m.update(image.read())
        num = int(m.hexdigest(), 16)
        md5_36 = base_n_encode(num, '0123456789abcdefghijklmnopqrstuvwxyz')
        pathname = settings.MEDIA_ROOT + '/images/' + md5_36[:2] + '/' + md5_36[2:4] + '/' + md5_36[4:6] + '/' + md5_36 + '.jpg'
        return pathname


