# authentic2 - versatile identity manager
# Copyright (C) 2010-2019 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import io
import warnings

import PIL.Image
from django.core.files import File
from django.forms import CharField, EmailField, FileField, ValidationError
from django.forms.fields import FILE_INPUT_CONTRADICTION
from django.utils.translation import ugettext_lazy as _

from authentic2 import app_settings
from authentic2.forms.widgets import (
    CheckPasswordInput,
    EmailInput,
    NewPasswordInput,
    PasswordInput,
    ProfileImageInput,
)
from authentic2.passwords import password_help_text, validate_password
from authentic2.validators import email_validator


class PasswordField(CharField):
    widget = PasswordInput


class NewPasswordField(CharField):
    widget = NewPasswordInput
    default_validators = [validate_password]

    def __init__(self, *args, **kwargs):
        kwargs['help_text'] = password_help_text()
        super().__init__(*args, **kwargs)


class CheckPasswordField(CharField):
    widget = CheckPasswordInput

    def __init__(self, *args, **kwargs):
        kwargs[
            'help_text'
        ] = '''
    <span class="a2-password-check-equality-default">%(default)s</span>
    <span class="a2-password-check-equality-matched">%(match)s</span>
    <span class="a2-password-check-equality-unmatched">%(nomatch)s</span>
''' % {
            'default': _('Both passwords must match.'),
            'match': _('Passwords match.'),
            'nomatch': _('Passwords do not match.'),
        }
        super().__init__(*args, **kwargs)


class ProfileImageField(FileField):
    widget = ProfileImageInput

    @property
    def image_size(self):
        return app_settings.A2_ATTRIBUTE_KIND_PROFILE_IMAGE_SIZE

    def clean(self, data, initial=None):
        if data is FILE_INPUT_CONTRADICTION or data is False or data is None:
            return super().clean(data, initial=initial)
        # we have a file
        try:
            with warnings.catch_warnings():
                image = PIL.Image.open(io.BytesIO(data.read()))
        except (OSError, PIL.Image.DecompressionBombWarning):
            raise ValidationError(_('The image is not valid'))
        image = self.normalize_image(image)
        new_data = self.file_from_image(image, data.name)
        return super().clean(new_data, initial=initial)

    def file_from_image(self, image, name=None):
        output = io.BytesIO()
        if image.mode != 'RGB':
            image = image.convert('RGB')
        image.save(output, format='JPEG', quality=99, optimize=1)
        output.seek(0)
        return File(output, name=name)

    def normalize_image(self, image):
        width = height = self.image_size
        if abs((1.0 * width / height) - (1.0 * image.size[0] / image.size[1])) > 0.1:
            # aspect ratio change, crop the image first
            box = [0, 0, image.size[0], int(image.size[0] * (1.0 * height / width))]

            if box[2] > image.size[0]:
                box = [int(t * (1.0 * image.size[0] / box[2])) for t in box]
            if box[3] > image.size[1]:
                box = [int(t * (1.0 * image.size[1] / box[3])) for t in box]

            if image.size[0] > image.size[1]:  # landscape
                box[0] = (image.size[0] - box[2]) / 2  # keep the middle
                box[2] += box[0]
            else:
                box[1] = (image.size[1] - box[3]) / 4  # keep mostly the top
                box[3] += box[1]

            image = image.crop(box)
        return image.resize([width, height], PIL.Image.ANTIALIAS)


class ValidatedEmailField(EmailField):
    default_validators = [email_validator]
    widget = EmailInput
