# -*- coding: utf-8 -*-
"""
    zine.forms
    ~~~~~~~~~~

    The form classes the zine core uses.

    :copyright: (c) 2010 by the Zine Team, see AUTHORS for more details.
    :license: BSD, see LICENSE for more details.
"""
from copy import copy
from datetime import datetime

from zine.i18n import _, lazy_gettext, list_languages
from zine.application import get_application, get_request, emit_event
from zine.config import DEFAULT_VARS
from zine.database import db, posts
from zine.models import User, Group, Comment, Post, Category, Tag, \
     NotificationSubscription, STATUS_DRAFT, STATUS_PUBLISHED, \
     STATUS_PROTECTED, STATUS_PRIVATE, \
     COMMENT_UNMODERATED, COMMENT_MODERATED, \
     COMMENT_BLOCKED_USER, COMMENT_BLOCKED_SPAM, COMMENT_DELETED
from zine.parsers import render_preview
from zine.privileges import bind_privileges
from zine.notifications import send_notification_template, NEW_COMMENT, \
     COMMENT_REQUIRES_MODERATION
from zine.utils import forms, log, dump_json
from zine.utils.http import redirect_to
from zine.utils.validators import ValidationError, is_valid_email, \
     is_valid_url, is_valid_slug, is_not_whitespace_only
from zine.utils.redirects import register_redirect, change_url_prefix


def config_field(cfgvar, label=None, **kwargs):
    """Helper function for fetching fields from the config."""
    if isinstance(cfgvar, forms.Field):
        field = copy(cfgvar)
    else:
        field = copy(DEFAULT_VARS[cfgvar])
    field._position_hint = forms._next_position_hint()
    if label is not None:
        field.label = label
    for name, value in kwargs.iteritems():
        setattr(field, name, value)
    return field


class LoginForm(forms.Form):
    """The form for the login page."""
    user = forms.ModelField(User, 'username', required=True, messages=dict(
        not_found=lazy_gettext(u'User “%(value)s” does not exist.'),
        required=lazy_gettext(u'You have to enter a username.')
    ), on_not_found=lambda user:
        log.warning(_(u'Failed login attempt, user “%s” does not exist')
                      % user, 'auth')
    )
    password = forms.TextField(widget=forms.PasswordInput)
    permanent = forms.BooleanField()

    def context_validate(self, data):
        if not data['user'].check_password(data['password']):
            log.warning(_(u'Failed login attempt from “%s”, invalid password')
                        % data['user'].username, 'auth')
            raise ValidationError(_('Incorrect password.'))


class ChangePasswordForm(forms.Form):
    """The form used on the password-change dialog in the admin panel."""
    old_password = forms.TextField(lazy_gettext(u'Old password'), required=True,
                                   widget=forms.PasswordInput)
    new_password = forms.TextField(lazy_gettext(u'New password'), required=True,
                                   widget=forms.PasswordInput)
    check_password = forms.TextField(lazy_gettext(u'Repeat password'),
                                     required=True,
                                     widget=forms.PasswordInput)

    def __init__(self, user, initial=None):
        forms.Form.__init__(self, initial)
        self.user = user

    def validate_old_password(self, value):
        if not self.user.check_password(value):
            raise ValidationError(_('The old password you\'ve '
                                    'entered is wrong.'))

    def context_validate(self, data):
        if data['new_password'] != data['check_password']:
            raise ValidationError(_('The two passwords don\'t match.'))


class NewCommentForm(forms.Form):
    """New comment form for authors."""
    # implementation detail: the maximum length of the column in the
    # database is longer than that.  However we don't want users to
    # insert too long names there.  The long column is reserved for
    # pingbacks and such.
    author = forms.TextField(lazy_gettext(u'Name*'), required=True,
                             max_length=100, messages=dict(
        too_long=lazy_gettext(u'Your name is too long.'),
        required=lazy_gettext(u'You have to enter your name.')
    ))
    email = forms.TextField(lazy_gettext(u'Mail* (not published)'),
                            required=True, validators=[is_valid_email()],
                            messages=dict(
        required=lazy_gettext(u'You have to enter a valid e-mail address.')
    ))
    www = forms.TextField(lazy_gettext(u'Website'), validators=[is_valid_url(
        message=lazy_gettext(u'You have to enter a valid URL or omit the field.')
    )])
    body = forms.TextField(lazy_gettext(u'Text'), min_length=2, max_length=6000,
                           required=True, messages=dict(
        too_short=lazy_gettext(u'Your comment is too short.'),
        too_long=lazy_gettext(u'Your comment is too long.'),
        required=lazy_gettext(u'You have to enter a comment.')
    ), widget=forms.Textarea)
    parent = forms.HiddenModelField(Comment)

    def __init__(self, post, user, initial=None):
        forms.Form.__init__(self, initial)
        self.req = get_request()
        self.post
 = post
     
   self.user = user

        # if the user is logged in the form is a bit smaller
        if user.is_somebody:
            del self.fields['author'], self.fields['email'], self.fields['www']

    def as_widget(self):
        widget = forms.Form.as_widget(self)
        widget.small_form = self.user.is_somebody
        return widget

    def validate_parent(self, value):
        if value.post != self.post:
            #_ this message is only displayed if the user tempered with
            #_ the form data
            raise ValidationError(_('Invalid object referenced.'))

    def context_validate(self, data):
        if not self.post.comments_enabled:
            raise ValidationError(_('Post is closed for commenting.'))
        if self.post.comments_closed:
            raise ValidationError(_('Commenting is no longer possible.'))

    def make_comment(self):
        """A handy helper to create a comment from the validated form."""
        ip = self.req and self.req.remote_addr or '0.0.0.0'
        if self.user.is_somebody:
            author = self.user
            email = www = None
        else:
            author = self['author']
            email = self['email']
            www = self['www']
        return Comment(self.post, author, self['body'], email, www,
                       self['parent'], submitter_ip=ip)

    def create_if_valid(self, req):
        """The one-trick pony for commenting.  Passed a req it tries to
        use the req data to submit a comment to the post.  If the req
        is not a post req or the form is invalid the return value is None,
        otherwise a redirect response to the new comment.
        """
        if req.method != 'POST' or not self.validate(req.form):
            return

        # if we don't have errors let's save it and emit an
        # `before-comment-saved` event so that plugins can do
        # block comments so that administrators have to approve it
        comment = self.make_comment()

        #! use this event to block comments before they are saved.  This
        #! is useful for antispam and other ways of moderation.
        emit_event('before-comment-saved', req, comment)

        # Moderate Comment?  Now that the spam check any everything
        # went through the processing we explicitly set it to
        # unmodereated if the blog configuration demands that
        if not comment.blocked and comment.requires_moderation:
            comment.status = COMMENT_UNMODERATED
            comment.blocked_msg = _(u'Comment waiting for approval')

        #! this is sent directly after the comment was saved.  Useful if
        #! you want to send mail notifications or whatever.
        emit_event('after-comment-saved', req, comment)

        # Commit so that make_visible_for_request can access the comment id.
        db.commit()

        # send out a notification if the comment is not spam.  Nobody is
        # interested in notifications on spam...
        if not comment.is_spam:
            if comment.blocked:
                noti