### codetube
    Copyright (C) 2011 payload payload@lavabit.com
    Copyright (C) 2011 dodo dodo.the.last@gmail.com

    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/>
###

gt = require('gettext')
forms = require('forms')
slug = require('slug')
helper = require('./helper')
appconfig = require('./appconfig')
config = require('./config')
async = require('async')
sha1 = require('./helper').hash('sha1')

# shortcuts

fields = forms.fields
widgets = forms.widgets
validators = forms.validators

# fields addons

fields.special = (text) ->
    field = new String(text)
    field.bind = () ->
        field.data = ''
        field.validate = (f, callback) ->
            callback(null, field)
        field
    field

# validators addons

validators.prefix = (prefix) ->
    (form, field, callback) ->
        return callback() unless field.data[...prefix.length] is prefix
        callback(gt._s(gt._("You can't use '%1' as prefix."), prefix))

validators.blacklist = (err_msg, blacklist) ->
    (form, field, callback) ->
        return callback() unless field.data in blacklist
        callback(gt._s(err_msg, field.data))

slug_validator = (validator) ->
    return ->
        checker = validator.apply(this, arguments)
        (form, field, callback) ->
            checker(form, data:slug(field.data), callback)


validators.slug_prefix = slug_validator(validators.prefix)
validators.slug_minlength = slug_validator(validators.minlength)
validators.slug_blacklist = slug_validator(async.apply(validators.blacklist, gt._("'%1' is blacklisted.")))
validators.new_value = async.apply(validators.blacklist, gt._("'%1' is already used."))
# patches

patch_ = (form, opts) ->
    old_bind = form.bind
    form.bind = (data) ->
        values = {}
        for own name, field of form.fields
            values[name] = field['default'] if field['default']?
        values[key] = value for own key, value of data or {}
        # FIXME until https://github.com/caolan/forms/issues/9
        if values.secure? and typeof values.secure is 'string'
            s = values.secure
            values.secure = false if s is 'false'
            values.secure = true if s is 'true'
        rv = old_bind.call(form, values)
        rv.clear_data = (req, args...) ->
            data = {}
            for own key, value of rv.data
                continue if key.charAt(0) is '_'
                unless opts
                    data[key] = value
                    continue
                unless opts[key] is off
                    if typeof opts[key] is 'function'
                        obj = opts[key].apply(this, [req, value].concat(args))
                        if obj
                            data[k] = v for own k, v of obj
                    else
                        data[key] = rv.data[key]
            data.clean = opts.clean if typeof opts?.clean is 'function'
            data
        rv
    form

patch_hashed_password_ = (form, opts) ->
    [password, session, confirm, unsecure] = [null, null, null, undefined]
    opts ?= {}
    sessionhash = (req, passwd) ->
        sha1.hash(session + passhash(req, passwd))
    passhash = (req, passwd) ->
        user = slug(req.body.user)
        if req.isAuthenticated()
            user or= req.session.auth.user.id
        sha1.hash(config.salt.public + user + passwd)


    opts.secure ?= (req, secure) ->
        form.fields.secure.data = no
        unsecure = not secure
        token = req.session.token
        req.session.token = null
        session = "off"
        session = token or "should" if secure
        res = {session}
        res.password = sessionhash(req, password) if password and unsecure
        res.password_uncrypted = passhash(req, confirm) if confirm and unsecure
        res

    opts.password ?= (req, passwd) ->
        password = passwd
        return {password:sessionhash(req, passwd)} if unsecure? and unsecure
        {password}

    opts.confirm ?= (req, passwd) ->
        confirm = passwd
        res =
            password_uncrypted: passwd
            post_validate: () ->
                if @password isnt sha1.hash(@session + @password_uncrypted)
                    form.fields.confirm.error = gt._('Does not match password.')
                this
        res.password_uncrypted = passhash(req, passwd) if unsecure? and unsecure
        res

    patch_(form, opts)

create_session_token = (req, res) ->
    #console.log {session:req.session}
    #return if req.session.token
    session =
        token:req.session.token = sha1.hash(Math.random() + config.salt.private)
        salt: config.salt.public
    res.expose(session, 'session')

# ---

enable_js = gt._("enable javascript to send your password hashed")

class Forms

    login: (req, res) ->
        create_session_token(req, res)
        form = forms.create
            _nojs: fields.special(enable_js)
            _js: fields.array(['jquery', 'passphrase'])
            secure: fields.boolean
                'default': no
                widget: widgets.hidden()
            user: fields.string
                required: yes
            password: fields.password(required: yes)
            _submit: fields.special('log in')
        patch_hashed_password_ form,
            user: (req, name) -> user:slug(name)

    register: (req, res) ->
        create_session_token(req, res)
        form = forms.create
            _nojs: fields.special(enable_js)
            _js: fields.array(['jquery', 'passphrase', 'slugify'])
            secure: fields.boolean
                'default': no
                widget: widgets.hidden()
            user: fields.string
                required: yes
                widget: widgets.text
                    classes: ["slugify"]
                validators: [
                    validators.slug_minlength(3)
                    validators.slug_prefix(appconfig.prefix)
                    validators.blacklist(appconfig.user_blacklist)
                    validators.slug_blacklist(appconfig.user_blacklist)
                ]
            password: fields.password
                required: yes
                validators: [validators.minlength(3)]
            confirm: fields.password
                required: yes
            _submit: fields.special('register')
        patch_hashed_password_ form,
            user: (req, name) -> {name, id:slug(name)}
            clean: () ->
                @password = @password_uncrypted
                delete @password_uncrypted
                this


    create_project: () ->
        form = forms.create
            _js: fields.array(['jquery', 'slugify'])
            name: fields.string
                required: yes
                widget: widgets.text
                    classes: ["slugify"]
                validators: [validators.slug_minlength(1)]
            description: fields.string()
            _submit: fields.special('create')
        patch_ form,
            name: (req, name) -> {name, id:slug(name)}

    # settings

    user_basic: () =>
        forms.create
            name: fields.string()
            _submit: fields.special('save')

    user_password: (req, res) ->
        create_session_token(req, res)
        form = forms.create
            _nojs: fields.special(enable_js)
            _js: fields.array(['jquery', 'passphrase'])
            secure: fields.boolean
                'default': no
                widget: widgets.hidden()
            user: fields.string
                'default': req.session?.auth?.user?.id
                required: yes
                widget: widgets.hidden()
            password: fields.password
                required: yes
                validators: [validators.minlength(3)]
            confirm: fields.password
                required: yes
            _submit: fields.special('save')
        patch_hashed_password_ form,
            user: off
            clean: () ->
                @passphrase = @password_uncrypted
                remove = [
                    'password_uncrypted'
                    'post_validate'
                    'password'
                    'session'
                    'clean'
                ]
                for key in remove
                    delete this[key]
                this

    user_email: (req, res, user) ->
        emails = user?.emails or {}
        form = forms.create
            _submit: fields.special('save')
            email_new: fields.email
                label: gt._("Add Email")
                validators: [
                    validators.new_value(x for x of emails)
                ]

        patch_ form,
            #email_new: (req, name) ->
            #    console.log("name", name)
            clean: (user) ->
                user.add_email(@email_new)
                return {"emails": user.emails}

    ###
    user_ssh: () =>
        emails = user?.ssh or {}
        defs = _submit: fields.special('save')
        for i, email of emails
            do (i, email) ->
                defs["ssh_#{i}"] = fields.email().bind(email.email)
        defs["ssh_new"] = fieds.email()
        forms.create(defs)
    ###

    project_basic: () ->
        form = forms.create
            name: fields.string
                required: yes
                validators: [validators.minlength(1)]
            description: fields.string()
            _submit: fields.special('save')
        patch_(form)

# exports

module.exports = new Forms()
