# Copyright 2014 Budapest University of Technology and Economics (BME IK)
#
# This file is part of CIRCLE Cloud.
#
# CIRCLE is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# CIRCLE 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 General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License along
# with CIRCLE.  If not, see <http://www.gnu.org/licenses/>.

from __future__ import absolute_import

from datetime import timedelta
from urlparse import urlparse

from django.contrib.auth.forms import (
    AuthenticationForm, PasswordResetForm, SetPasswordForm,
    PasswordChangeForm,
)
from django.contrib.auth.models import User, Group
from django.core.validators import URLValidator
from django.core.exceptions import PermissionDenied, ValidationError

import autocomplete_light
from crispy_forms.helper import FormHelper
from crispy_forms.layout import (
    Layout, Div, BaseInput, Field, HTML, Submit, TEMPLATE_PACK,
)

from crispy_forms.utils import render_field
from django import forms
from django.contrib.auth.forms import UserCreationForm as OrgUserCreationForm
from django.forms.widgets import TextInput, HiddenInput
from django.template import Context
from django.template.loader import render_to_string
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from sizefield.widgets import FileSizeWidget
from django.core.urlresolvers import reverse_lazy

from django_sshkey.models import UserKey
from firewall.models import Vlan, Host
from vm.models import (
    InstanceTemplate, Lease, InterfaceTemplate, Node, Trait, Instance
)
from django.contrib.admin.widgets import FilteredSelectMultiple
from django.contrib.auth.models import Permission
from .models import Profile, GroupProfile
from circle.settings.base import LANGUAGES, MAX_NODE_RAM
from django.utils.translation import string_concat

from .validators import domain_validator

from dashboard.models import ConnectCommand

LANGUAGES_WITH_CODE = ((l[0], string_concat(l[1], " (", l[0], ")"))
                       for l in LANGUAGES)

priority_choices = (
    (10, _("idle")),
    (30, _("normal")),
    (80, _("server")),
    (100, _("realtime")),
)


class VmSaveForm(forms.Form):
    name = forms.CharField(max_length=100, label=_('Name'),
                           help_text=_('Human readable name of template.'))

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper

    def __init__(self, *args, **kwargs):
        default = kwargs.pop('default', None)
        super(VmSaveForm, self).__init__(*args, **kwargs)
        if default:
            self.fields['name'].initial = default


class VmCustomizeForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={
        'class': "form-control",
        'style': "max-width: 350px",
        'required': "",
    }))

    cpu_count = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control input-tags cpu-count-input",
        'min': 1,
        'max': 10,
        'required': "",
    }),
        min_value=1, max_value=10,
    )

    ram_size = forms.IntegerField(widget=forms.TextInput(attrs={
        'class': "form-control input-tags ram-input",
        'min': 128,
        'pattern': "\d+",
        'max': MAX_NODE_RAM,
        'step': 128,
        'required': "",
    }),
        min_value=128, max_value=MAX_NODE_RAM,
    )

    cpu_priority = forms.ChoiceField(
        priority_choices, widget=forms.Select(attrs={
            'class': "form-control input-tags cpu-priority-input",
        })
    )

    amount = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control",
        'min': "1",
        'style': "max-width: 60px",
        'required': "",
    }), initial=1, min_value=1)

    disks = forms.ModelMultipleChoiceField(
        queryset=None, required=False,
        widget=forms.SelectMultiple(attrs={
            'class': "form-control",
            'id': "vm-create-disk-add-form",
        })
    )
    networks = forms.ModelMultipleChoiceField(
        queryset=None, required=False,
        widget=forms.SelectMultiple(attrs={
            'class': "form-control",
            'id': "vm-create-network-add-vlan",
        })
    )

    template = forms.CharField(widget=forms.HiddenInput())
    customized = forms.CharField(widget=forms.HiddenInput())

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user", None)
        self.template = kwargs.pop("template", None)
        super(VmCustomizeForm, self).__init__(*args, **kwargs)

        if self.user.has_perm("vm.set_resources"):
            self.allowed_fields = tuple(self.fields.keys())
            # set displayed disk and network list
            self.fields['disks'].queryset = self.template.disks.all()
            self.fields['networks'].queryset = Vlan.get_objects_with_level(
                'user', self.user)

            # set initial for disk and network list
            self.initial['disks'] = self.template.disks.all()
            self.initial['networks'] = InterfaceTemplate.objects.filter(
                template=self.template).values_list("vlan", flat=True)

            # set initial for resources
            self.initial['cpu_priority'] = self.template.priority
            self.initial['cpu_count'] = self.template.num_cores
            self.initial['ram_size'] = self.template.ram_size

        else:
            self.allowed_fields = ("name", "template", "customized", )

        # initial name and template pk
        self.initial['name'] = self.template.name
        self.initial['template'] = self.template.pk
        self.initial['customized'] = True

    def _clean_fields(self):
        for name, field in self.fields.items():
            if name in self.allowed_fields:
                value = field.widget.value_from_datadict(
                    self.data, self.files, self.add_prefix(name))
                try:
                    value = field.clean(value)
                    self.cleaned_data[name] = value
                    if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value
                except ValidationError as e:
                    self._errors[name] = self.error_class(e.messages)
                    if name in self.cleaned_data:
                        del self.cleaned_data[name]


class GroupCreateForm(forms.ModelForm):

    description = forms.CharField(label=_("Description"), required=False,
                                  widget=forms.Textarea(attrs={'rows': 3}))

    def __init__(self, *args, **kwargs):
        new_groups = kwargs.pop('new_groups', None)
        super(GroupCreateForm, self).__init__(*args, **kwargs)
        choices = [('', '--')]
        if new_groups:
            choices += [(g, g) for g in new_groups if len(g) <= 64]
        self.fields['org_id'] = forms.ChoiceField(
            # TRANSLATORS: directory like in LDAP
            choices=choices, required=False, label=_('Directory identifier'))
        if new_groups:
            self.fields['org_id'].help_text = _(
                "If you select an item here, the members of this directory "
                "group will be automatically added to the group at the time "
                "they log in. Please note that other users (those with "
                "permissions like yours) may also automatically become a "
                "group co-owner).")
        else:
            self.fields['org_id'].widget = HiddenInput()

    def save(self, commit=True):
        if not commit:
            raise AttributeError('Committing is mandatory.')
        group = super(GroupCreateForm, self).save()

        profile = group.profile
        # multiple blanks were not be unique unlike NULLs are
        profile.org_id = self.cleaned_data['org_id'] or None
        profile.description = self.cleaned_data['description']
        profile.save()

        return group

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.add_input(Submit("submit", _("Create")))
        helper.form_tag = False
        return helper

    class Meta:
        model = Group
        fields = ('name', )


class GroupProfileUpdateForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        new_groups = kwargs.pop('new_groups', None)
        superuser = kwargs.pop('superuser', False)
        super(GroupProfileUpdateForm, self).__init__(*args, **kwargs)
        if not superuser:
            choices = [('', '--')]
            if new_groups:
                choices += [(g, g) for g in new_groups if len(g) <= 64]
            self.fields['org_id'] = forms.ChoiceField(
                choices=choices, required=False,
                label=_('Directory identifier'))
            if not new_groups:
                self.fields['org_id'].widget = HiddenInput()
        self.fields['description'].widget = forms.Textarea(attrs={'rows': 3})

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.add_input(Submit("submit", _("Save")))
        helper.form_tag = False
        return helper

    def save(self, commit=True):
        profile = super(GroupProfileUpdateForm, self).save(commit=False)
        profile.org_id = self.cleaned_data['org_id'] or None
        if commit:
            profile.save()
        return profile

    class Meta:
        model = GroupProfile
        fields = ('description', 'org_id')


class HostForm(forms.ModelForm):

    def setowner(self, user):
        self.instance.owner = user

    def __init__(self, *args, **kwargs):
        super(HostForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_show_labels = False
        self.helper.form_tag = False
        self.helper.layout = Layout(
            Div(
                Div(  # host
                    Div(
                        AnyTag(
                            'h3',
                            HTML(_("Host")),
                        ),
                        css_class="col-sm-3",
                    ),
                    css_class="row",
                ),
                Div(  # host data
                    Div(  # hostname
                        HTML('<label for="node-hostname-box">'
                             'Name'
                             '</label>'),
                        css_class="col-sm-3",
                    ),
                    Div(  # hostname
                        'hostname',
                        css_class="col-sm-9",
                    ),
                    Div(  # mac
                        HTML('<label for="node-mac-box">'
                             'MAC'
                             '</label>'),
                        css_class="col-sm-3",
                    ),
                    Div(
                        'mac',
                        css_class="col-sm-9",
                    ),
                    Div(  # ip
                        HTML('<label for="node-ip-box">'
                             'IP'
                             '</label>'),
                        css_class="col-sm-3",
                    ),
                    Div(
                        'ipv4',
                        css_class="col-sm-9",
                    ),
                    Div(  # vlan
                        HTML('<label for="node-vlan-box">'
                             'VLAN'
                             '</label>'),
                        css_class="col-sm-3",
                    ),
                    Div(
                        'vlan',
                        css_class="col-sm-9",
                    ),
                    css_class="row",
                ),
            ),
        )

    class Meta:
        model = Host
        fields = ['hostname', 'vlan', 'mac', 'ipv4', ]


class NodeForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(NodeForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_show_labels = False
        self.helper.layout = Layout(
            Div(
                Div(
                    Div(
                        Div(
                            AnyTag(
                                'h3',
                                HTML(_("Node")),
                            ),
                            css_class="col-sm-3",
                        ),
                        css_class="row",
                    ),
                    Div(
                        Div(  # nodename
                            HTML('<label for="node-nodename-box">'
                                 'Name'
                                 '</label>'),
                            css_class="col-sm-3",
                        ),
                        Div(
                            'name',
                            css_class="col-sm-9",
                        ),
                        css_class="row",
                    ),
                    Div(
                        Div(  # priority
                            HTML('<label for="node-nodename-box">'
                                 'Priority'
                                 '</label>'),
                            css_class="col-sm-3",
                        ),
                        Div(
                            'priority',
                            css_class="col-sm-9",
                        ),
                        css_class="row",
                    ),
                    Div(
                        Div(  # enabled
                            HTML('<label for="node-nodename-box">'
                                 'Enabled'
                                 '</label>'),
                            css_class="col-sm-3",
                        ),
                        Div(
                            'enabled',
                            css_class="col-sm-9",
                        ),
                        css_class="row",
                    ),
                    Div(  # nested host
                        HTML("""{% load crispy_forms_tags %}
                            {% crispy hostform %}
                            """)
                    ),
                    Div(
                        Div(
                            AnyTag(  # tip: don't try to use Button class
                                "button",
                                AnyTag(
                                    "i",
                                    css_class="fa fa-play"
                                ),
                                HTML("Start"),
                                css_id="node-create-submit",
                                css_class="btn btn-success",
                            ),
                            css_class="col-sm-12 text-right",
                        ),
                        css_class="row",
                    ),
                    css_class="col-sm-11",
                ),
                css_class="row",
            ),
        )

    class Meta:
        model = Node
        fields = ['name', 'priority', 'enabled', ]


class TemplateForm(forms.ModelForm):
    networks = forms.ModelMultipleChoiceField(
        queryset=None, required=False, label=_("Networks"))

    num_cores = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control input-tags cpu-count-input",
        'min': 1,
        'max': 10,
        'required': "",
    }),
        min_value=1, max_value=10,
    )

    ram_size = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control input-tags ram-input",
        'min': 128,
        'max': MAX_NODE_RAM,
        'step': 128,
        'required': "",
    }),
        min_value=128, max_value=MAX_NODE_RAM,
    )

    priority = forms.ChoiceField(priority_choices, widget=forms.Select(attrs={
        'class': "form-control input-tags cpu-priority-input",
    }))

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user", None)
        super(TemplateForm, self).__init__(*args, **kwargs)

        self.fields['networks'].queryset = Vlan.get_objects_with_level(
            'user', self.user)

        data = self.data.copy()
        data['owner'] = self.user.pk
        self.data = data

        if self.instance.pk:
            n = self.instance.interface_set.values_list("vlan", flat=True)
            self.initial['networks'] = n

        if self.instance.pk and not self.instance.has_level(self.user,
                                                            'owner'):
            self.allowed_fields = ()
        else:
            self.allowed_fields = (
                'name', 'access_method', 'description', 'system', 'tags',
                'arch', 'lease', 'has_agent')
        if (self.user.has_perm('vm.change_template_resources')
                or not self.instance.pk):
            self.allowed_fields += tuple(set(self.fields.keys()) -
                                         set(['raw_data']))
        if self.user.is_superuser:
            self.allowed_fields += ('raw_data', )
        for name, field in self.fields.items():
            if name not in self.allowed_fields:
                field.widget.attrs['disabled'] = 'disabled'

        if not self.instance.pk and len(self.errors) < 1:
            self.initial['num_cores'] = 1
            self.initial['priority'] = 10
            self.initial['ram_size'] = 512
            self.initial['max_ram_size'] = 512

        lease_queryset = (
            Lease.get_objects_with_level("operator", self.user).distinct()
            | Lease.objects.filter(pk=self.instance.lease_id).distinct())

        self.fields["lease"].queryset = lease_queryset

        self.fields['raw_data'].validators.append(domain_validator)

    def clean_owner(self):
        if self.instance.pk is not None:
            return User.objects.get(pk=self.instance.owner.pk)
        return self.user

    def clean_max_ram_size(self):
        return self.cleaned_data.get("ram_size", 0)

    def _clean_fields(self):
        try:
            old = InstanceTemplate.objects.get(pk=self.instance.pk)
        except InstanceTemplate.DoesNotExist:
            old = None
        for name, field in self.fields.items():
            if name in self.allowed_fields:
                value = field.widget.value_from_datadict(
                    self.data, self.files, self.add_prefix(name))
                try:
                    value = field.clean(value)
                    self.cleaned_data[name] = value
                    if hasattr(self, 'clean_%s' % name):
                        value = getattr(self, 'clean_%s' % name)()
                        self.cleaned_data[name] = value
                except ValidationError as e:
                    self._errors[name] = self.error_class(e.messages)
                    if name in self.cleaned_data:
                        del self.cleaned_data[name]
            elif old:
                if name == 'networks':
                    self.cleaned_data[name] = [
                        i.vlan for i in self.instance.interface_set.all()]
                else:
                    self.cleaned_data[name] = getattr(old, name)

        if "req_traits" not in self.allowed_fields:
            self.cleaned_data['req_traits'] = self.instance.req_traits.all()

    def save(self, commit=True):
        data = self.cleaned_data
        self.instance.max_ram_size = data.get('ram_size')

        instance = super(TemplateForm, self).save(commit=True)

        # create and/or delete InterfaceTemplates
        networks = InterfaceTemplate.objects.filter(
            template=self.instance).values_list("vlan", flat=True)
        for m in data['networks']:
            if not m.has_level(self.user, "user"):
                raise PermissionDenied()
            if m.pk not in networks:
                InterfaceTemplate(vlan=m, managed=m.managed,
                                  template=self.instance).save()
        InterfaceTemplate.objects.filter(
            template=self.instance).exclude(
            vlan__in=data['networks']).delete()

        return instance

    @property
    def helper(self):
        submit_kwargs = {}
        if self.instance.pk and not self.instance.has_level(self.user,
                                                            'owner'):
            submit_kwargs['disabled'] = None

        helper = FormHelper()
        return helper

    class Meta:
        model = InstanceTemplate
        exclude = ('state', 'disks', )
        widgets = {
            'system': forms.TextInput,
            'max_ram_size': forms.HiddenInput,
            'parent': forms.Select(attrs={'disabled': ""}),
        }


class LeaseForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(LeaseForm, self).__init__(*args, **kwargs)
        self.generate_fields()

    # e2ae8b048e7198428f696375b8bdcd89e90002d1/django/utils/timesince.py#L10
    def get_intervals(self, delta_seconds):
        chunks = (
            (60 * 60 * 24 * 30, "months"),
            (60 * 60 * 24 * 7, "weeks"),
            (60 * 60 * 24, "days"),
            (60 * 60, "hours"),
        )
        for i, (seconds, name) in enumerate(chunks):
            count = delta_seconds // seconds
            if count != 0:
                break
        re = {'%s' % name: count}
        if i + 1 < len(chunks) and i > 0:
            seconds2, name2 = chunks[i + 1]
            count2 = (delta_seconds - (seconds * count)) // seconds2
            if count2 != 0:
                re['%s' % name2] = count2
        return re

    def generate_fields(self):
        intervals = ["hours", "days", "weeks", "months"]
        methods = ["suspend", "delete"]
        # feels redundant but these lines are so long
        s = (self.instance.suspend_interval.total_seconds()
             if self.instance.pk else 0)
        d = (self.instance.delete_interval.total_seconds()
             if self.instance.pk else 0)
        seconds = {
            'suspend': s,
            'delete': d
        }
        initial = {
            'suspend': self.get_intervals(int(seconds['suspend'])),
            'delete': self.get_intervals(int(seconds['delete']))
        }
        for m in methods:
            for idx, i in enumerate(intervals):
                self.fields["%s_%s" % (m, i)] = forms.IntegerField(
                    min_value=0, widget=NumberInput,
                    initial=initial[m].get(i, 0))

    def save(self, commit=True):
        data = self.cleaned_data

        suspend_seconds = timedelta(
            hours=data['suspend_hours'],
            days=(data['suspend_days'] + data['suspend_months'] % 12 * 30 +
                  data['suspend_months'] / 12 * 365),
            weeks=data['suspend_weeks'],
        )
        delete_seconds = timedelta(
            hours=data['delete_hours'],
            days=(data['delete_days'] + data['delete_months'] % 12 * 30 +
                  data['delete_months'] / 12 * 365),
            weeks=data['delete_weeks'],
        )
        self.instance.delete_interval = delete_seconds
        self.instance.suspend_interval = suspend_seconds
        instance = super(LeaseForm, self).save(commit=False)
        if commit:
            instance.save()
        return instance

    @property
    def helper(self):
        helper = FormHelper()
        helper.layout = Layout(
            Field('name'),
            Field("suspend_interval_seconds", type="hidden", value="0"),
            Field("delete_interval_seconds", type="hidden", value="0"),
            HTML(string_concat("<label>", _("Suspend in"), "</label>")),
            Div(
                NumberField("suspend_hours", css_class="form-control"),
                Div(
                    HTML(_("hours")),
                    css_class="input-group-addon",
                ),
                NumberField("suspend_days", css_class="form-control"),
                Div(
                    HTML(_("days")),
                    css_class="input-group-addon",
                ),
                NumberField("suspend_weeks", css_class="form-control"),
                Div(
                    HTML(_("weeks")),
                    css_class="input-group-addon",
                ),
                NumberField("suspend_months", css_class="form-control"),
                Div(
                    HTML(_("months")),
                    css_class="input-group-addon",
                ),
                css_class="input-group interval-input",
            ),
            HTML(string_concat("<label>", _("Delete in"), "</label>")),
            Div(
                NumberField("delete_hours", css_class="form-control"),
                Div(
                    HTML(_("hours")),
                    css_class="input-group-addon",
                ),
                NumberField("delete_days", css_class="form-control"),
                Div(
                    HTML(_("days")),
                    css_class="input-group-addon",
                ),
                NumberField("delete_weeks", css_class="form-control"),
                Div(
                    HTML(_("weeks")),
                    css_class="input-group-addon",
                ),
                NumberField("delete_months", css_class="form-control"),
                Div(
                    HTML(_("months")),
                    css_class="input-group-addon",
                ),
                css_class="input-group interval-input",
            )
        )
        helper.add_input(Submit("submit", _("Save changes")))
        return helper

    class Meta:
        model = Lease


class VmRenewForm(forms.Form):

    force = forms.BooleanField(required=False, label=_(
        "Set expiration times even if they are shorter than "
        "the current value."))
    save = forms.BooleanField(required=False, label=_(
        "Save selected lease."))

    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('choices')
        default = kwargs.pop('default')
        super(VmRenewForm, self).__init__(*args, **kwargs)

        self.fields.insert(0, 'lease', forms.ModelChoiceField(
            queryset=choices, initial=default, required=False,
            empty_label=None, label=_('Length')))
        if len(choices) < 2:
            self.fields['lease'].widget = HiddenInput()
            self.fields['save'].widget = HiddenInput()

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper


class VmMigrateForm(forms.Form):
    live_migration = forms.BooleanField(
        required=False, initial=True, label=_("live migration"))

    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('choices')
        default = kwargs.pop('default')
        super(VmMigrateForm, self).__init__(*args, **kwargs)

        self.fields.insert(0, 'to_node', forms.ModelChoiceField(
            queryset=choices, initial=default, required=False,
            widget=forms.RadioSelect(), label=_("Node")))


class VmStateChangeForm(forms.Form):

    interrupt = forms.BooleanField(required=False, label=_(
        "Forcibly interrupt all running activities."),
        help_text=_("Set all activities to finished state, "
                    "but don't interrupt any tasks."))
    new_state = forms.ChoiceField(Instance.STATUS, label=_(
        "New status"))
    reset_node = forms.BooleanField(required=False, label=_("Reset node"))

    def __init__(self, *args, **kwargs):
        show_interrupt = kwargs.pop('show_interrupt')
        status = kwargs.pop('status')
        super(VmStateChangeForm, self).__init__(*args, **kwargs)

        if not show_interrupt:
            self.fields['interrupt'].widget = HiddenInput()
        self.fields['new_state'].initial = status

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper


class RedeployForm(forms.Form):
    with_emergency_change_state = forms.BooleanField(
        required=False, initial=True, label=_("use emergency state change"))

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper


class VmCreateDiskForm(forms.Form):
    name = forms.CharField(max_length=100, label=_("Name"))
    size = forms.CharField(
        widget=FileSizeWidget, initial=(10 << 30), label=_('Size'),
        help_text=_('Size of disk to create in bytes or with units '
                    'like MB or GB.'))

    def __init__(self, *args, **kwargs):
        default = kwargs.pop('default', None)
        super(VmCreateDiskForm, self).__init__(*args, **kwargs)
        if default:
            self.fields['name'].initial = default

    def clean_size(self):
        size_in_bytes = self.cleaned_data.get("size")
        if not size_in_bytes.isdigit() and len(size_in_bytes) > 0:
            raise forms.ValidationError(_("Invalid format, you can use "
                                          " GB or MB!"))
        return size_in_bytes

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper


class VmDiskResizeForm(forms.Form):
    size = forms.CharField(
        widget=FileSizeWidget, initial=(10 << 30), label=_('Size'),
        help_text=_('Size to resize the disk in bytes or with units '
                    'like MB or GB.'))

    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('choices')
        self.disk = kwargs.pop('default')

        super(VmDiskResizeForm, self).__init__(*args, **kwargs)

        self.fields.insert(0, 'disk', forms.ModelChoiceField(
            queryset=choices, initial=self.disk, required=True,
            empty_label=None, label=_('Disk')))
        if self.disk:
            self.fields['disk'].widget = HiddenInput()
            self.fields['size'].initial += self.disk.size

    def clean(self):
        cleaned_data = super(VmDiskResizeForm, self).clean()
        size_in_bytes = self.cleaned_data.get("size")
        disk = self.cleaned_data.get('disk')
        if not size_in_bytes.isdigit() and len(size_in_bytes) > 0:
            raise forms.ValidationError(_("Invalid format, you can use "
                                          " GB or MB!"))
        if int(size_in_bytes) < int(disk.size):
            raise forms.ValidationError(_("Disk size must be greater than the "
                                        "actual size."))
        return cleaned_data

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        if self.disk:
            helper.layout = Layout(
                HTML(_("<label>Disk:</label> %s") % escape(self.disk)),
                Field('disk'), Field('size'))
        return helper


class VmDiskRemoveForm(forms.Form):
    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('choices')
        self.disk = kwargs.pop('default')

        super(VmDiskRemoveForm, self).__init__(*args, **kwargs)

        self.fields.insert(0, 'disk', forms.ModelChoiceField(
            queryset=choices, initial=self.disk, required=True,
            empty_label=None, label=_('Disk')))
        if self.disk:
            self.fields['disk'].widget = HiddenInput()

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        if self.disk:
            helper.layout = Layout(
                AnyTag(
                    "div",
                    HTML(_("<label>Disk:</label> %s") % escape(self.disk)),
                    css_class="form-group",
                ),
                Field("disk"),
            )
        return helper


class VmDownloadDiskForm(forms.Form):
    name = forms.CharField(max_length=100, label=_("Name"), required=False)
    url = forms.CharField(label=_('URL'), validators=[URLValidator(), ])

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper

    def clean(self):
        cleaned_data = super(VmDownloadDiskForm, self).clean()
        if not cleaned_data['name']:
            if cleaned_data['url']:
                cleaned_data['name'] = urlparse(
                    cleaned_data['url']).path.split('/')[-1]
            if not cleaned_data['name']:
                raise forms.ValidationError(
                    _("Could not find filename in URL, "
                      "please specify a name explicitly."))
        return cleaned_data


class VmAddInterfaceForm(forms.Form):
    def __init__(self, *args, **kwargs):
        choices = kwargs.pop('choices')
        super(VmAddInterfaceForm, self).__init__(*args, **kwargs)

        field = forms.ModelChoiceField(
            queryset=choices, required=True, label=_('Vlan'))
        if not choices:
            field.widget.attrs['disabled'] = 'disabled'
            field.empty_label = _('No more networks.')
        self.fields['vlan'] = field

    @property
    def helper(self):
        helper = FormHelper(self)
        helper.form_tag = False
        return helper


class CircleAuthenticationForm(AuthenticationForm):
    # fields: username, password

    @property
    def helper(self):
        helper = FormHelper()
        helper.form_show_labels = False
        helper.layout = Layout(
            AnyTag(
                "div",
                AnyTag(
                    "span",
                    AnyTag(
                        "i",
                        css_class="fa fa-user",
                    ),
                    css_class="input-group-addon",
                ),
                Field("username", placeholder=_("Username"),
                      css_class="form-control"),
                css_class="input-group",
            ),
            AnyTag(
                "div",
                AnyTag(
                    "span",
                    AnyTag(
                        "i",
                        css_class="fa fa-lock",
                    ),
                    css_class="input-group-addon",
                ),
                Field("password", placeholder=_("Password"),
                      css_class="form-control"),
                css_class="input-group",
            ),
        )
        helper.add_input(Submit("submit", _("Sign in"),
                                css_class="btn btn-success"))
        return helper


class CirclePasswordResetForm(PasswordResetForm):
    # fields: email

    @property
    def helper(self):
        helper = FormHelper()
        helper.form_show_labels = False
        helper.layout = Layout(
            AnyTag(
                "div",
                AnyTag(
                    "span",
                    AnyTag(
                        "i",
                        css_class="fa fa-envelope",
                    ),
                    css_class="input-group-addon",
                ),
                Field("email", placeholder=_("Email address"),
                      css_class="form-control"),
                Div(
                    AnyTag(
                        "button",
                        HTML(_("Reset password")),
                        css_class="btn btn-success",
                    ),
                    css_class="input-group-btn",
                ),
                css_class="input-group",
            ),
        )
        return helper


class CircleSetPasswordForm(SetPasswordForm):

    @property
    def helper(self):
        helper = FormHelper()
        helper.add_input(Submit("submit", _("Change password"),
                                css_class="btn btn-success change-password",
                                css_id="submit-password-button"))
        return helper


class LinkButton(BaseInput):

    """
    Used to create a link button descriptor for the {% crispy %} template tag::

        back = LinkButton('back', 'Back', reverse_lazy('index'))

    .. note:: The first argument is also slugified and turned into the id for
              the submit button.
    """
    template = "bootstrap/layout/linkbutton.html"
    field_classes = 'btn btn-default'

    def __init__(self, name, text, url, *args, **kwargs):
        self.href = url
        super(LinkButton, self).__init__(name, text, *args, **kwargs)


class NumberInput(TextInput):
    input_type = "number"


class NumberField(Field):
    template = "crispy_forms/numberfield.html"

    def __init__(self, *args, **kwargs):
        kwargs['min'] = 0
        super(NumberField, self).__init__(*args, **kwargs)


class AnyTag(Div):
    template = "crispy_forms/anytag.html"

    def __init__(self, tag, *fields, **kwargs):
        self.tag = tag
        super(AnyTag, self).__init__(*fields, **kwargs)

    def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
        fields = ''
        for field in self.fields:
            fields += render_field(field, form, form_style, context,
                                   template_pack=template_pack)

        return render_to_string(self.template, Context({'tag': self,
                                                        'fields': fields}))


class WorkingBaseInput(BaseInput):

    def __init__(self, name, value, input_type="text", **kwargs):
        self.input_type = input_type
        self.field_classes = ""  # we need this for some reason
        super(WorkingBaseInput, self).__init__(name, value, **kwargs)


class TraitForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(TraitForm, self).__init__(*args, **kwargs)
        self.helper = FormHelper(self)
        self.helper.form_show_labels = False
        self.helper.layout = Layout(
            Div(
                Field('name', id="node-details-traits-input",
                      css_class="input-sm input-traits"),
                Div(
                    Submit("submit", _("Add trait"),
                           css_class="btn btn-primary btn-sm input-traits"),
                    css_class="input-group-btn",
                ),
                css_class="input-group",
                id="node-details-traits-form",
            ),
        )

    class Meta:
        model = Trait
        fields = ['name']


class MyProfileForm(forms.ModelForm):
    preferred_language = forms.ChoiceField(LANGUAGES_WITH_CODE)

    class Meta:
        fields = ('preferred_language', 'email_notifications',
                  'use_gravatar', )
        model = Profile

    @property
    def helper(self):
        helper = FormHelper()
        helper.add_input(Submit("submit", _("Save")))
        return helper

    def save(self, *args, **kwargs):
        value = super(MyProfileForm, self).save(*args, **kwargs)
        return value


class UnsubscribeForm(forms.ModelForm):

    class Meta:
        fields = ('email_notifications', )
        model = Profile

    @property
    def helper(self):
        helper = FormHelper()
        helper.add_input(Submit("submit", _("Save")))
        return helper


class CirclePasswordChangeForm(PasswordChangeForm):

    @property
    def helper(self):
        helper = FormHelper()
        helper.add_input(Submit("submit", _("Change password"),
                                css_class="btn btn-primary",
                                css_id="submit-password-button"))
        return helper


class UserCreationForm(OrgUserCreationForm):

    class Meta:
        model = User
        fields = ("username", 'email', 'first_name', 'last_name')

    @property
    def helper(self):
        helper = FormHelper()
        helper.layout = Layout('username', 'password1', 'password2', 'email',
                               'first_name', 'last_name')
        helper.add_input(Submit("submit", _("Save")))
        return helper

    def save(self, commit=True):
        user = super(UserCreationForm, self).save(commit=False)
        user.set_password(self.cleaned_data["password1"])
        if commit:
            user.save()
        return user


class AclUserOrGroupAddForm(forms.Form):
    name = forms.CharField(widget=autocomplete_light.TextWidget(
        'AclUserGroupAutocomplete',
        autocomplete_js_attributes={'placeholder': _("Name of group or user")},
        attrs={'class': 'form-control'}))


class TransferOwnershipForm(forms.Form):
    name = forms.CharField(
        widget=autocomplete_light.TextWidget(
            'AclUserAutocomplete',
            autocomplete_js_attributes={"placeholder": _("Name of user")},
            attrs={'class': 'form-control'}),
        label=_("E-mail address or identifier of user"))


class AddGroupMemberForm(forms.Form):
    new_member = forms.CharField(
        widget=autocomplete_light.TextWidget(
            'AclUserAutocomplete',
            autocomplete_js_attributes={"placeholder": _("Name of user")},
            attrs={'class': 'form-control'}),
        label=_("E-mail address or identifier of user"))


class UserKeyForm(forms.ModelForm):
    name = forms.CharField(required=True, label=_('Name'))
    key = forms.CharField(
        label=_('Key'), required=True,
        help_text=_('For example: ssh-rsa AAAAB3NzaC1yc2ED...'),
        widget=forms.Textarea(attrs={'rows': 5}))

    class Meta:
        fields = ('name', 'key')
        model = UserKey

    @property
    def helper(self):
        helper = FormHelper()
        helper.add_input(Submit("submit", _("Save")))
        return helper

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user", None)
        super(UserKeyForm, self).__init__(*args, **kwargs)

    def clean(self):
        if self.user:
            self.instance.user = self.user
        return super(UserKeyForm, self).clean()


class ConnectCommandForm(forms.ModelForm):
    class Meta:
        fields = ('name', 'access_method', 'template')
        model = ConnectCommand

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user")
        super(ConnectCommandForm, self).__init__(*args, **kwargs)

    def clean(self):
        if self.user:
            self.instance.user = self.user

        return super(ConnectCommandForm, self).clean()


class TraitsForm(forms.ModelForm):

    class Meta:
        model = Instance
        fields = ('req_traits', )

    @property
    def helper(self):
        helper = FormHelper()
        helper.form_show_labels = False
        helper.form_action = reverse_lazy("dashboard.views.vm-traits",
                                          kwargs={'pk': self.instance.pk})
        helper.add_input(Submit("submit", _("Save"),
                                css_class="btn btn-success", ))
        return helper


class RawDataForm(forms.ModelForm):
    raw_data = forms.CharField(validators=[domain_validator],
                               widget=forms.Textarea(attrs={'rows': 5}),
                               required=False)

    class Meta:
        model = Instance
        fields = ('raw_data', )

    @property
    def helper(self):
        helper = FormHelper()
        helper.form_show_labels = False
        helper.form_action = reverse_lazy("dashboard.views.vm-raw-data",
                                          kwargs={'pk': self.instance.pk})
        helper.add_input(Submit("submit", _("Save"),
                                css_class="btn btn-success",
                                css_id="submit-password-button"))
        return helper


permissions_filtered = Permission.objects.exclude(
    codename__startswith="add_").exclude(
    codename__startswith="delete_").exclude(
    codename__startswith="change_")


class GroupPermissionForm(forms.ModelForm):
    permissions = forms.ModelMultipleChoiceField(
        queryset=permissions_filtered,
        widget=FilteredSelectMultiple(_("permissions"), is_stacked=False)
    )

    class Meta:
        model = Group
        fields = ('permissions', )

    @property
    def helper(self):
        helper = FormHelper()
        helper.form_show_labels = False
        helper.form_action = reverse_lazy(
            "dashboard.views.group-permissions",
            kwargs={'group_pk': self.instance.pk})
        helper.add_input(Submit("submit", _("Save"),
                                css_class="btn btn-success", ))
        return helper


class VmResourcesForm(forms.ModelForm):
    num_cores = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control input-tags cpu-count-input",
        'min': 1,
        'max': 10,
        'required': "",
    }),
        min_value=1, max_value=10,
    )

    ram_size = forms.IntegerField(widget=forms.NumberInput(attrs={
        'class': "form-control input-tags ram-input",
        'min': 128,
        'max': MAX_NODE_RAM,
        'step': 128,
        'required': "",
    }),
        min_value=128, max_value=MAX_NODE_RAM,
    )

    priority = forms.ChoiceField(priority_choices, widget=forms.Select(attrs={
        'class': "form-control input-tags cpu-priority-input",
    }))

    def __init__(self, *args, **kwargs):
        self.can_edit = kwargs.pop("can_edit", None)
        super(VmResourcesForm, self).__init__(*args, **kwargs)

        if not self.can_edit:
            for name, field in self.fields.items():
                field.widget.attrs['disabled'] = "disabled"

    class Meta:
        model = Instance
        fields = ('num_cores', 'priority', 'ram_size', )


vm_search_choices = (
    ("owned", _("owned")),
    ("shared", _("shared")),
    ("all", _("all")),
)


class VmListSearchForm(forms.Form):
    s = forms.CharField(widget=forms.TextInput(attrs={
        'class': "form-control input-tags",
        'placeholder': _("Search...")
    }))

    stype = forms.ChoiceField(vm_search_choices, widget=forms.Select(attrs={
        'class': "btn btn-default form-control input-tags",
        'style': "min-width: 80px;",
    }))

    include_deleted = forms.BooleanField(widget=forms.CheckboxInput(attrs={
        'id': "vm-list-search-checkbox",
    }))

    def __init__(self, *args, **kwargs):
        super(VmListSearchForm, self).__init__(*args, **kwargs)
        # set initial value, otherwise it would be overwritten by request.GET
        if not self.data.get("stype"):
            data = self.data.copy()
            data['stype'] = "all"
            self.data = data


class TemplateListSearchForm(forms.Form):
    s = forms.CharField(widget=forms.TextInput(attrs={
        'class': "form-control input-tags",
        'placeholder': _("Search...")
    }))

    stype = forms.ChoiceField(vm_search_choices, widget=forms.Select(attrs={
        'class': "btn btn-default input-tags",
    }))

    def __init__(self, *args, **kwargs):
        super(TemplateListSearchForm, self).__init__(*args, **kwargs)
        # set initial value, otherwise it would be overwritten by request.GET
        if not self.data.get("stype"):
            data = self.data.copy()
            data['stype'] = "owned"
            self.data = data