"""
Iguana (c) by Marc Ammon, Moritz Fickenscher, Lukas Fridolin,
Michael Gunselmann, Katrin Raab, Christian Strate

Iguana is licensed under a
Creative Commons Attribution-ShareAlike 4.0 International License.

You should have received a copy of the license along with this
work. If not, see <http://creativecommons.org/licenses/by-sa/4.0/>.
"""

from django.core.exceptions import ValidationError
# exif stripping
from django.core.files.uploadedfile import InMemoryUploadedFile
import io
from PIL import Image
# file type verification
import magic
from common.settings import (ALLOWED_IMG_EXTENSIONS, MAX_IMG_SIZE_BASE, MAXIMUM_IMG_SIZE,
                             MAX_FILE_SIZE_BASE, MAXIMUM_FILE_SIZE)

from django.utils.translation import ugettext_lazy as _


def create_img(in_memory_img, format_str, suffix_str, content_type):
    # remove any possible suffixes to avoid possible confusion
    img_name = in_memory_img.name.partition(".")[0]
    img = Image.open(in_memory_img)
    img_io_bytes = io.BytesIO()
    # img.save(img_io_bytes, format=format_str)
    # new_img = InMemoryUploadedFile(img_io_bytes, None, img_name+suffix_str, content_type,
    #                                img_io_bytes.getbuffer().nbytes, None)
    # store the image always as jpeg
    # transform the alpha channel to white
    if img.mode in ('RGBA', 'LA'):
        img_wo_alpha = Image.new(img.mode[:-1], img.size, '#ffffff')
        img_wo_alpha.paste(img, img.split()[-1])
        # TODO should img get closed?
        img = img_wo_alpha

    # TODO I'm not sure yet whether this sanitizes the image too
    img.convert('RGB').save(img_io_bytes, format="JPEG")
    new_img = InMemoryUploadedFile(img_io_bytes, None, img_name+".jpg", "image/jpeg",
                                   img_io_bytes.getbuffer().nbytes, None)
    new_img.seek(0)
    img.close()
    in_memory_img.close()
    return new_img


# It is necessary to strip any meta information before the image is stored for the first time. In many other
# approaches those information are striped after the image has been stored. Hence a leak would be possible for a short
# amount of time. Therefore InMemory representation is used to prevent any leaks.
# \param in_memory_file the file that has been uploaded
# \param has_to_be_an_image bool that signalises if the file has been uploaded via the CustomImageField
#        or if it is a general attachment
def return_in_memory_file(in_memory_file, has_to_be_an_image):
    if not in_memory_file:
        return None

    # file size limitation
    if has_to_be_an_image:
        if in_memory_file.size > MAXIMUM_IMG_SIZE:
            raise ValidationError(_("The uploaded image exceeds the allowed file size of: ") +
                                  str(MAX_IMG_SIZE_BASE)+" MB", code='file_too_big')
    else:
        # it is possible to upload larger images as an attachment
        if in_memory_file.size > MAXIMUM_FILE_SIZE:
            raise ValidationError(_("The uploaded file exceeds the allowed file size of: ") +
                                  str(MAX_FILE_SIZE_BASE)+" MB", code='file_too_big')

    content_type = ""
    """
    if type(in_memory_file) == InMemoryUploadedFile:
        content_type = in_memory_file.content_type
    else:
        # XXX some testcases seem to create files with wrong file format. In case of erroneous magic
        #     bytes we do not strip any data.
        # TODO is there something we can do about that?
        pass
    """

    # "Like any data supplied by the user, you shouldn't trust that the uploaded file is actually this type.
    # You’ll still need to validate that the file contains the content that the content-type header claims -
    # “trust but verify.”"    therefore the actual type is checked
    # Normally chunks() is preferred over read(), since the later one has some DOS-potential because of the
    # huge memory usage for big files. But this should be fine since there is a file size limitation.
    img_type = magic.from_buffer(in_memory_file.read(in_memory_file.size))
    in_memory_file.seek(0)

    # bmp
    if "PC bitmap" in img_type:
        """
        if content_type != "image/bmp":
            raise ValidationError(_("There is a mismatch between the file header and content-type header"),
                                  code='img_type_missmatch')
        """
        return create_img(in_memory_file, "BMP", ".bmp", content_type)

    # jpe, jpeg, jpg
    if "JPEG image data" in img_type:
        """
        if content_type != "image/jpeg" and content_type != "image/jpg":
            raise ValidationError(_("There is a mismatch between the file header and content-type header"),
                                  code='img_type_missmatch')
        """
        return create_img(in_memory_file, "JPEG", ".jpg", content_type)

    # gif
    if "GIF image data" in img_type:
        """
        if content_type != "image/gif":
            raise ValidationError(_("There is a mismatch between the file header and content-type header"),
                                  code='img_type_missmatch')
        """
        return create_img(in_memory_file, "GIF", ".gif", content_type)
    # png
    if "PNG image data" in img_type:
        """
        if content_type != "image/png":
            raise ValidationError(_("There is a mismatch between the file header and content-type header"),
                                  code='img_type_missmatch')
        """
        return create_img(in_memory_file, "PNG", ".png", content_type)

    """
    # tif, tiff
    if content_type == "image/tiff":
        if "TIFF image data" not in img_type:
            raise ValidationError(_("There is a mismatch between the file header and content-type header"),
                                  code='img_type_missmatch')
        return create_img(in_memory_file, "TIFF", ".tiff", content_type)
    # pbm, pgm, ppm
    if "Netpbm image data" in img_type:
        return create_img(in_memory_file, "PPM", ".ppm", content_type)
    """

    # since svg is not a valid extension it can not be uploaded. Hence this image has to be the default-avatar
    if "VG Scalable Vector Graphics image" in img_type:
        in_memory_file.close()
        return in_memory_file

    # The file has been uploaded as a general attachment
    # TODO This is not perfect yet, there are additional image file formats.
    #      Also this doesn't stop malicious code to be uploaded
    if not has_to_be_an_image:
        return in_memory_file

    # The file has been uploaded via the CustomImageField and no supported image matched the file type
    raise ValidationError(_("Either unable to detect the image type or the image type is not supported. " +
                            "Supported image extensions are: ") + str(ALLOWED_IMG_EXTENSIONS), code='unknown_img_type')


def strip_if_file_is_an_img(in_memory_file):
    return return_in_memory_file(in_memory_file, False)


def strip_img_metadata(in_memory_img):
    return return_in_memory_file(in_memory_img, True)