from __future__ import unicode_literals

import os
import shutil
from unittest import skipIf

from django.core.exceptions import ImproperlyConfigured, ValidationError
from django.core.files import File
from django.core.files.images import ImageFile
from django.test import TestCase
from django.test.testcases import SerializeMixin
from django.utils._os import upath

try:
    from .models import Image
except ImproperlyConfigured:
    Image = None

if Image:
    from .models import (
        Person, PersonWithHeight, PersonWithHeightAndWidth,
        PersonDimensionsFirst, PersonTwoImages, TestImageFieldFile,
    )
    from .models import temp_storage_dir
else:
    # Pillow not available, create dummy classes (tests will be skipped anyway)
    class Person():
        pass
    PersonWithHeight = PersonWithHeightAndWidth = PersonDimensionsFirst = Person
    PersonTwoImages = Person


class ImageFieldTestMixin(SerializeMixin):
    """
    Mixin class to provide common functionality to ImageField test classes.
    """

    lockfile = __file__

    # Person model to use for tests.
    PersonModel = PersonWithHeightAndWidth
    # File class to use for file instances.
    File = ImageFile

    def setUp(self):
        """
        Creates a pristine temp directory (or deletes and recreates if it
        already exists) that the model uses as its storage directory.

        Sets up two ImageFile instances for use in tests.
        """
        if os.path.exists(temp_storage_dir):
            shutil.rmtree(temp_storage_dir)
        os.mkdir(temp_storage_dir)

        file_path1 = os.path.join(os.path.dirname(upath(__file__)), "4x8.png")
        self.file1 = self.File(open(file_path1, 'rb'), name='4x8.png')

        file_path2 = os.path.join(os.path.dirname(upath(__file__)), "8x4.png")
        self.file2 = self.File(open(file_path2, 'rb'), name='8x4.png')

    def tearDown(self):
        """
        Removes temp directory and all its contents.
        """
        self.file1.close()
        self.file2.close()
        shutil.rmtree(temp_storage_dir)

    def check_dimensions(self, instance, width, height,
                         field_name='mugshot'):
        """
        Asserts that the given width and height values match both the
        field's height and width attributes and the height and width fields
        (if defined) the image field is caching to.

        Note, this method will check for dimension fields named by adding
        "_width" or "_height" to the name of the ImageField.  So, the
        models used in these tests must have their fields named
        accordingly.

        By default, we check the field named "mugshot", but this can be
        specified by passing the field_name parameter.
        """
        field = getattr(instance, field_name)
        # Check height/width attributes of field.
        if width is None and height is None:
            with self.assertRaises(ValueError):
                getattr(field, 'width')
            with self.assertRaises(ValueError):
                getattr(field, 'height')
        else:
            self.assertEqual(field.width, width)
            self.assertEqual(field.height, height)

        # Check height/width fields of model, if defined.
        width_field_name = field_name + '_width'
        if hasattr(instance, width_field_name):
            self.assertEqual(getattr(instance, width_field_name), width)
        height_field_name = field_name + '_height'
        if hasattr(instance, height_field_name):
            self.assertEqual(getattr(instance, height_field_name), height)


@skipIf(Image is None, "Pillow is required to test ImageField")
class ImageFieldTests(ImageFieldTestMixin, TestCase):
    """
    Tests for ImageField that don't need to be run with each of the
    different test model classes.
    """

    def test_equal_notequal_hash(self):
        """
        Bug #9786: Ensure '==' and '!=' work correctly.
        Bug #9508: make sure hash() works as expected (equal items must
        hash to the same value).
        """
        # Create two Persons with different mugshots.
        p1 = self.PersonModel(name="Joe")
        p1.mugshot.save("mug", self.file1)
        p2 = self.PersonModel(name="Bob")
        p2.mugshot.save("mug", self.file2)
        self.assertIs(p1.mugshot == p2.mugshot, False)
        self.assertIs(p1.mugshot != p2.mugshot, True)

        # Test again with an instance fetched from the db.
        p1_db = self.PersonModel.objects.get(name="Joe")
        self.assertIs(p1_db.mugshot == p2.mugshot, False)
        self.assertIs(p1_db.mugshot != p2.mugshot, True)