#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright (c) 2008-2018 University of Dundee.
#
# 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/>.
#
# Author: Aleksandra Tarkowska <A(dot)Tarkowska(at)dundee(dot)ac(dot)uk>, 2008.
#
# Version: 1.0
#

import logging

try:
    from collections import OrderedDict  # Python 2.7+ only
except Exception:
    pass

from django import forms
from django.forms.widgets import Textarea

from omeroweb.connector import Server

from omeroweb.custom_forms import NonASCIIForm

from .custom_forms import ServerModelChoiceField, GroupModelChoiceField
from .custom_forms import GroupModelMultipleChoiceField, OmeNameField
from .custom_forms import ExperimenterModelMultipleChoiceField, MultiEmailField

logger = logging.getLogger(__name__)

#################################################################
# Non-model Form


class LoginForm(NonASCIIForm):
    def __init__(self, *args, **kwargs):
        super(LoginForm, self).__init__(*args, **kwargs)
        self.fields["server"] = ServerModelChoiceField(Server, empty_label=None)

        self.fields.keyOrder = ["server", "username", "password"]

    username = forms.CharField(
        max_length=50,
        widget=forms.TextInput(attrs={"size": 22, "autofocus": "autofocus"}),
    )
    password = forms.CharField(
        max_length=50,
        widget=forms.PasswordInput(attrs={"size": 22, "autocomplete": "off"}),
    )

    def clean_username(self):
        if self.cleaned_data["username"] == "guest":
            raise forms.ValidationError("Guest account is not supported.")
        return self.cleaned_data["username"]


class ForgottonPasswordForm(NonASCIIForm):
    def __init__(self, *args, **kwargs):
        super(ForgottonPasswordForm, self).__init__(*args, **kwargs)
        self.fields["server"] = ServerModelChoiceField(Server, empty_label=None)
        f = forms.CharField(
            max_length=50,
            widget=forms.TextInput(attrs={"size": 28, "autocomplete": "off"}),
        )
        self.fields["username"] = f
        self.fields["email"] = forms.EmailField(
            widget=forms.TextInput(attrs={"size": 28, "autocomplete": "off"})
        )


ROLE_CHOICES = (
    ("user", "User"),
    ("administrator", "Administrator"),
    ("restricted_administrator", "Administrator with restricted privileges"),
)


class ExperimenterForm(NonASCIIForm):
    def __init__(
        self,
        name_check=False,
        email_check=False,
        experimenter_is_me_or_system=False,
        experimenter_me=False,
        can_modify_user=True,
        user_privileges=[],
        experimenter_root=False,
        *args,
        **kwargs
    ):
        super(ExperimenterForm, self).__init__(*args, **kwargs)
        self.name_check = name_check
        self.email_check = email_check
        self.user_privileges = user_privileges

        try:
            self.fields["other_groups"] = GroupModelMultipleChoiceField(
                queryset=kwargs["initial"]["groups"],
                initial=kwargs["initial"]["other_groups"],
                required=False,
                label="Groups",
            )
        except Exception:
            self.fields["other_groups"] = GroupModelMultipleChoiceField(
                queryset=kwargs["initial"]["groups"], required=False, label="Groups"
            )

        try:
            self.fields["default_group"] = GroupModelChoiceField(
                queryset=kwargs["initial"]["my_groups"],
                initial=kwargs["initial"]["default_group"],
                empty_label="",
                required=False,
            )
        except Exception:
            try:
                self.fields["default_group"] = GroupModelChoiceField(
                    queryset=kwargs["initial"]["my_groups"],
                    empty_label="",
                    required=False,
                )
            except Exception:
                self.fields["default_group"] = GroupModelChoiceField(
                    queryset=list(), empty_label="", required=False
                )

        # 'Role' is disabled if experimenter is 'admin' or self,
        # so required=False to avoid validation error.
        # If current user is restricted Admin, can't create full Admin
        restricted_admin = "ReadSession" not in self.user_privileges
        self.fields["role"] = forms.ChoiceField(
            choices=ROLE_CHOICES,
            widget=forms.RadioSelect(
                {"disabled": restricted_admin or experimenter_root}
            ),
            required=False,
            initial="user",
        )

        if "with_password" in kwargs["initial"] and kwargs["initial"]["with_password"]:
            self.fields["password"] = forms.CharField(
                max_length=50,
                widget=forms.PasswordInput(attrs={"size": 30, "autocomplete": "off"}),
            )
            self.fields["confirmation"] = forms.CharField(
                max_length=50,
                widget=forms.PasswordInput(attrs={"size": 30, "autocomplete": "off"}),
            )

            fields_key_order = [
                "omename",
                "password",
                "confirmation",
                "first_name",
                "middle_name",
                "last_name",
                "email",
                "institution",
                "role",
                "active",
                "other_groups",
                "default_group",
            ]
        else:
            fields_key_order = [
                "omename",
                "first_name",
                "middle_name",
                "last_name",
                "email",
                "institution",
                "role",
                "active",
                "other_groups",
                "default_group",
            ]

        ordered_fields = [(k, self.fields[k]) for k in fields_key_order]

        roles = [
            ("Sudo", "Sudo"),
            # combine WriteFile/ManagedRepo/Owned roles into 'Write'
            ("Write", "Write Data"),
            # combine DeleteFile/ManagedRepo/Owned roles into 'Delete'
            ("Delete", "Delete Data"),
            ("Chgrp", "Chgrp"),
            ("Chown", "Chown"),
            ("ModifyGroup", "Create and Edit Groups"),
            ("ModifyUser", "Create and Edit Users"),
            ("ModifyGroupMembership", "Add Users to Groups"),
            ("Script", "Upload Scripts"),
        ]
        for role in roles:
            # If current user is light-admin, ignore privileges they don't have
            # So they can't add/remove these from experimenter
            # We don't disable them - (not in form data and will be removed)
            ordered_fields.append(
                (
                    role[0],
                    forms.BooleanField(
                        required=False,
                        label=role[1],
                        widget=forms.CheckboxInput(
                            attrs={
                                "class": "privilege",
                                "disabled": role[0] not in user_privileges,
                            }
                        ),
                    ),
                )
            )

        # Django 1.8: Form.fields uses OrderedDict from the collections module.
        self.fields = OrderedDict(ordered_fields)

        if experimenter_me or experimenter_root:
            self.fields["omename"].widget.attrs["readonly"] = True
            name = "yourself"
            if experimenter_root:
                name = "'root' user"
            self.fields["omename"].widget.attrs["title"] = (
                "You can't edit Username of %s" % name
            )
            self.fields["role"].widget.attrs["disabled"] = True
            self.fields["active"].widget.attrs["disabled"] = True
            self.fields["active"].widget.attrs["title"] = "You cannot disable %s" % name

        # If we can't modify user, ALL fields are disabled
        if not can_modify_user:
            for field in self.fields.values():
                field.widget.attrs["disabled"] = True

    omename = OmeNameField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        label="Username",
    )
    first_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
    )
    middle_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )
    last_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
    )
    email = forms.EmailField(
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )
    institution = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )

    active = forms.BooleanField(required=False)

    def clean_confirmation(self):
        if self.cleaned_data.get("password"):
            if len(self.cleaned_data.get("password")) < 3:
                raise forms.ValidationError(
                    "Password must be at least 3 characters long."
                )
        if self.cleaned_data.get("password") or self.cleaned_data.get("confirmation"):
            if self.cleaned_data.get("password") != self.cleaned_data.get(
                "confirmation"
            ):
                raise forms.ValidationError("Passwords do not match")
            else:
                return self.cleaned_data.get("password")

    def clean_omename(self):
        if self.name_check:
            raise forms.ValidationError("This username already exists.")
        return self.cleaned_data.get("omename")

    def clean_email(self):
        if self.email_check:
            raise forms.ValidationError("This email already exists.")
        return self.cleaned_data.get("email")

    def clean_default_group(self):
        if (
            self.cleaned_data.get("default_group") is None
            or len(self.cleaned_data.get("default_group")) <= 0
        ):
            raise forms.ValidationError("No default group selected.")
        else:
            return self.cleaned_data.get("default_group")

    def clean_other_groups(self):
        if (
            self.cleaned_data.get("other_groups") is None
            or len(self.cleaned_data.get("other_groups")) <= 0
        ):
            raise forms.ValidationError("User must be a member of at least one group.")
        else:
            return self.cleaned_data.get("other_groups")


PERMISSION_CHOICES = (
    ("0", "Private"),
    ("1", "Read-Only"),
    ("2", "Read-Annotate"),
    ("3", "Read-Write"),
)


class GroupForm(NonASCIIForm):
    def __init__(
        self,
        name_check=False,
        group_is_system=False,
        can_modify_group=True,
        can_add_member=True,
        *args,
        **kwargs
    ):
        super(GroupForm, self).__init__(*args, **kwargs)

        self.name_check = name_check
        if can_modify_group:

            self.fields["name"] = forms.CharField(
                max_length=100,
                widget=forms.TextInput(attrs={"size": 25, "autocomplete": "off"}),
            )
            self.fields["description"] = forms.CharField(
                max_length=250,
                required=False,
                widget=forms.TextInput(attrs={"size": 25, "autocomplete": "off"}),
            )
        if can_add_member:
            try:
                if kwargs["initial"]["owners"]:
                    pass
                self.fields["owners"] = ExperimenterModelMultipleChoiceField(
                    queryset=kwargs["initial"]["experimenters"],
                    initial=kwargs["initial"]["owners"],
                    required=False,
                )
            except Exception:
                self.fields["owners"] = ExperimenterModelMultipleChoiceField(
                    queryset=kwargs["initial"]["experimenters"], required=False
                )

            try:
                if kwargs["initial"]["members"]:
                    pass
                self.fields["members"] = ExperimenterModelMultipleChoiceField(
                    queryset=kwargs["initial"]["experimenters"],
                    initial=kwargs["initial"]["members"],
                    required=False,
                )
            except Exception:
                self.fields["members"] = ExperimenterModelMultipleChoiceField(
                    queryset=kwargs["initial"]["experimenters"], required=False
                )

        if can_modify_group:
            self.fields["permissions"] = forms.ChoiceField(
                choices=PERMISSION_CHOICES,
                widget=forms.RadioSelect(),
                required=True,
                label="Permissions",
            )

        if group_is_system:
            self.fields["name"].widget.attrs["readonly"] = True
            self.fields["name"].widget.attrs[
                "title"
            ] = "Changing of system group name would be un-doable"

        self.fields.keyOrder = [
            "name",
            "description",
            "owners",
            "members",
            "permissions",
        ]

        # If we can't modify group, disable fields
        if not can_modify_group:
            for name, field in self.fields.items():
                if name not in ("owners", "members"):
                    field.widget.attrs["disabled"] = True

    def clean_name(self):
        if self.name_check:
            raise forms.ValidationError("This name already exists.")
        return self.cleaned_data.get("name")


class GroupOwnerForm(forms.Form):
    def __init__(self, *args, **kwargs):
        super(GroupOwnerForm, self).__init__(*args, **kwargs)

        try:
            if kwargs["initial"]["owners"]:
                pass
            self.fields["owners"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"],
                initial=kwargs["initial"]["owners"],
                required=False,
            )
        except Exception:
            self.fields["owners"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"], required=False
            )

        try:
            if kwargs["initial"]["members"]:
                pass
            self.fields["members"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"],
                initial=kwargs["initial"]["members"],
                required=False,
            )
        except Exception:
            self.fields["members"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"], required=False
            )

        self.fields.keyOrder = ["owners", "members", "permissions"]

    permissions = forms.ChoiceField(
        choices=PERMISSION_CHOICES,
        widget=forms.RadioSelect(),
        required=True,
        label="Permissions",
    )


class MyAccountForm(NonASCIIForm):
    def __init__(self, email_check=False, *args, **kwargs):
        super(MyAccountForm, self).__init__(*args, **kwargs)
        self.email_check = email_check
        try:
            if kwargs["initial"]["default_group"]:
                pass
            self.fields["default_group"] = GroupModelChoiceField(
                queryset=kwargs["initial"]["groups"],
                initial=kwargs["initial"]["default_group"],
                empty_label=None,
            )
        except Exception:
            self.fields["default_group"] = GroupModelChoiceField(
                queryset=kwargs["initial"]["groups"], empty_label=None
            )
        self.fields.keyOrder = [
            "omename",
            "first_name",
            "middle_name",
            "last_name",
            "email",
            "institution",
            "default_group",
        ]

    omename = forms.CharField(
        max_length=50,
        widget=forms.TextInput(
            attrs={"onfocus": "this.blur()", "size": 30, "autocomplete": "off"}
        ),
        label="Username",
    )
    first_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
    )
    middle_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )
    last_name = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
    )
    email = forms.EmailField(
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )
    institution = forms.CharField(
        max_length=250,
        widget=forms.TextInput(attrs={"size": 30, "autocomplete": "off"}),
        required=False,
    )

    def clean_email(self):
        if self.email_check:
            raise forms.ValidationError("This email already exists.")
        return self.cleaned_data.get("email")


class ContainedExperimentersForm(NonASCIIForm):
    def __init__(self, *args, **kwargs):
        super(ContainedExperimentersForm, self).__init__(*args, **kwargs)

        try:
            if kwargs["initial"]["members"]:
                pass
            self.fields["members"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"],
                initial=kwargs["initial"]["members"],
                required=False,
            )
        except Exception:
            self.fields["members"] = ExperimenterModelMultipleChoiceField(
                queryset=kwargs["initial"]["experimenters"], required=False
            )

        self.fields.keyOrder = ["members"]


class UploadPhotoForm(forms.Form):

    photo = forms.FileField(required=False)

    def clean_photo(self):
        if self.cleaned_data.get("photo") is None:
            raise forms.ValidationError(
                "No image selected. Supported image formats (file extensions"
                " allowed): jpeg, jpg, gif, png. The maximum image size"
                " allowed is 200KB."
            )
        if not self.cleaned_data.get("photo").content_type.startswith("image"):
            raise forms.ValidationError(
                "Supported image formats (file extensions allowed):"
                " jpeg, jpg, gif, png."
            )
        if self.cleaned_data.get("photo").size > 204800:
            raise forms.ValidationError("The maximum image size allowed is 200KB.")
        return self.cleaned_data.get("photo")


class ChangePassword(NonASCIIForm):

    old_password = forms.CharField(
        max_length=50,
        widget=forms.PasswordInput(attrs={"size": 30, "autocomplete": "off"}),
        label="Current password",
    )
    password = forms.CharField(
        max_length=50,
        widget=forms.PasswordInput(attrs={"size": 30, "autocomplete": "off"}),
        label="New password",
    )
    confirmation = forms.CharField(
        max_length=50,
        widget=forms.PasswordInput(attrs={"size": 30, "autocomplete": "off"}),
        label="Confirm password",
    )

    def clean_confirmation(self):
        if self.cleaned_data.get("password") or self.cleaned_data.get("confirmation"):
            if len(self.cleaned_data.get("password")) < 3:
                raise forms.ValidationError(
                    "Password must be at least 3" " characters long."
                )
            if self.cleaned_data.get("password") != self.cleaned_data.get(
                "confirmation"
            ):
                raise forms.ValidationError("Passwords do not match")
            else:
                return self.cleaned_data.get("password")


class EnumerationEntry(NonASCIIForm):

    new_entry = forms.CharField(
        max_length=250, widget=forms.TextInput(attrs={"size": 30})
    )


class EnumerationEntries(NonASCIIForm):
    def __init__(self, entries, *args, **kwargs):
        super(EnumerationEntries, self).__init__(*args, **kwargs)
        for i, e in enumerate(entries):
            try:
                if kwargs["initial"]["entries"]:
                    self.fields[str(e.id)] = forms.CharField(
                        max_length=250,
                        initial=e.value,
                        widget=forms.TextInput(attrs={"size": 30}),
                        label=i + 1,
                    )
                else:
                    self.fields[str(e.id)] = forms.CharField(
                        max_length=250,
                        widget=forms.TextInput(attrs={"size": 30}),
                        label=i + 1,
                    )
            except Exception:
                self.fields[str(e.id)] = forms.CharField(
                    max_length=250,
                    widget=forms.TextInput(attrs={"size": 30}),
                    label=i + 1,
                )

        self.fields.keyOrder = [str(k) for k in self.fields.keys()]


class EmailForm(forms.Form):
    """
    Form to gather recipients, subject and message for sending email
    announcements
    """

    error_css_class = "field-error"
    required_css_class = "field-required"

    # Define these as None just so I can order them
    everyone = forms.BooleanField(required=False, label="All Users")

    experimenters = forms.TypedMultipleChoiceField(
        required=False, coerce=int, label="Users"
    )
    groups = forms.TypedMultipleChoiceField(required=False, coerce=int)

    # TODO CC isn't really CC. Maybe change label or change functionality
    cc = MultiEmailField(required=False)
    subject = forms.CharField(max_length=100, required=True)
    message = forms.CharField(widget=Textarea, required=True)

    # Include/Exclude inactive users
    inactive = forms.BooleanField(label="Include inactive users", required=False)

    def __init__(self, experimenters, groups, conn, request, *args, **kwargs):
        super(EmailForm, self).__init__(*args, **kwargs)
        # Process Experimenters/Groups into choices (lists of tuples)
        self.fields["experimenters"].choices = [
            (
                experimenter.id,
                experimenter.firstName
                + " "
                + experimenter.lastName
                + " ("
                + experimenter.omeName
                + ")"
                + (" - Inactive" if not experimenter.isActive() else ""),
            )
            for experimenter in experimenters
        ]

        self.fields["groups"].choices = [(group.id, group.name) for group in groups]

        self.conn = conn
        self.request = request

    def clean(self):
        cleaned_data = super(EmailForm, self).clean()
        everyone = cleaned_data.get("everyone")
        experimenters = cleaned_data.get("experimenters")
        groups = cleaned_data.get("groups")
        cc = cleaned_data.get("cc")

        # If nobody addressed, throw an error
        if not cc and not everyone and not experimenters and not groups:
            raise forms.ValidationError(
                "At least one addressee must be "
                "specified in one or more of 'all',"
                " 'user', 'group' or 'cc'"
            )
        return cleaned_data
