var edx = edx || {};

(function($, _, Backbone, gettext) {
    'use strict';

    edx.student = edx.student || {};
    edx.student.account = edx.student.account || {};

    edx.student.account.FormView = Backbone.View.extend({
        tagName: 'form',

        el: '',

        tpl: '',

        fieldTpl: '#form_field-tpl',

        events: {},

        errors: [],

        formType: '',

        $form: {},

        fields: [],

        // String to append to required label fields
        requiredStr: '*',

        submitButton: '',

        initialize: function( data ) {
            this.model = data.model;
            this.preRender( data );

            this.tpl = $(this.tpl).html();
            this.fieldTpl = $(this.fieldTpl).html();
            this.buildForm( data.fields );

            this.listenTo( this.model, 'error', this.saveError );
        },

        /* Allows extended views to add custom
         * init steps without needing to repeat
         * default init steps
         */
        preRender: function( data ) {
            /* Custom code goes here */
            return data;
        },

        render: function( html ) {
            var fields = html || '';

            $(this.el).html( _.template( this.tpl, {
                fields: fields
            }));

            this.postRender();

            return this;
        },

        postRender: function() {
            var $container = $(this.el);

            this.$form = $container.find('form');
            this.$errors = $container.find('.submission-error');
            this.$submitButton = $container.find(this.submitButton);
        },

        buildForm: function( data ) {
            var html = [],
                i,
                len = data.length,
                fieldTpl = this.fieldTpl;

            this.fields = data;

            for ( i=0; i<len; i++ ) {
                if ( data[i].errorMessages ) {
                    data[i].errorMessages = this.escapeStrings( data[i].errorMessages );
                }

                html.push( _.template( fieldTpl, $.extend( data[i], {
                    form: this.formType,
                    requiredStr: this.requiredStr
                }) ) );
            }

            this.render( html.join('') );
        },

        /* Helper method to toggle display
         * including accessibility considerations
         */
        element: {
            hide: function( $el ) {
                if ( $el ) {
                    $el.addClass('hidden');
                }
            },

            scrollTop: function( $el ) {
                // Scroll to top of selected element
                $('html,body').animate({
                    scrollTop: $el.offset().top
                },'slow');
            },

            show: function( $el ) {
                if ( $el ) {
                    $el.removeClass('hidden');
                }
            }
        },

        escapeStrings: function( obj ) {
            _.each( obj, function( val, key ) {
                obj[key] = _.escape( val );
            });

            return obj;
        },

        focusFirstError: function() {
            var $error = this.$form.find('.error').first(),
                $field = {},
                $parent = {};

            if ( $error.is('label') ) {
                $parent = $error.parent('.form-field');
                $error = $parent.find('input') || $parent.find('select');
            } else {
                $field = $error;
            }

            $error.focus();
        },

        forgotPassword: function( event ) {
            event.preventDefault();

            this.trigger('password-help');
        },

        getFormData: function() {
            var obj = {},
                $form = this.$form,
                elements = $form[0].elements,
                i,
                len = elements.length,
                $el,
                $label,
                key = '',
                errors = [],
                test = {};

            for ( i=0; i<len; i++ ) {

                $el = $( elements[i] );
                $label = $form.find('label[for=' + $el.attr('id') + ']');
                key = $el.attr('name') || false;

                if ( key ) {
                    test = this.validate( elements[i] );
                    if ( test.isValid ) {
                        obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val();
                        $el.removeClass('error');
                        $label.removeClass('error');
                    } else {
                        errors.push( test.message );
                        $el.addClass('error');
                        $label.addClass('error');
                    }
                }
            }

            this.errors = _.uniq( errors );

            return obj;
        },

        saveError: function( error ) {
            this.errors = ['<li>' + error.responseText + '</li>'];
            this.setErrors();
            this.toggleDisableButton(false);
        },

        setErrors: function() {
            var $msg = this.$errors.find('.message-copy'),
                html = [],
                errors = this.errors,
                i,
                len = errors.length;

            for ( i=0; i<len; i++ ) {
                html.push( errors[i] );
            }

            $msg.html( html.join('') );

            this.element.show( this.$errors );

            // Scroll to error messages
            $('html,body').animate({
                scrollTop: this.$errors.offset().top
            },'slow');

            // Focus on first error field
            this.focusFirstError();
        },

        submitForm: function( event ) {
            var data = this.getFormData();

            event.preventDefault();

            this.toggleDisableButton(true);

            if ( !_.compact(this.errors).length ) {
                this.model.set( data );
                this.model.save();
                this.toggleErrorMsg( false );
            } else {
                this.toggleErrorMsg( true );
            }

            this.postFormSubmission();
        },

        /* Allows extended views to add custom
         * code after form submission
         */
        postFormSubmission: function() {
            return true;
        },

        toggleErrorMsg: function( show ) {
            if ( show ) {
                this.setErrors();
                this.toggleDisableButton(false);
            } else {
                this.element.hide( this.$errors );
            }
        },

        /**
         * If a form button is defined for this form, this will disable the button on
         * submit, and re-enable the button if an error occurs.
         *
         * Args:
         *      disabled (boolean): If set to TRUE, disable the button.
         *
         */
        toggleDisableButton: function ( disabled ) {
            if (this.$submitButton) {
                this.$submitButton.attr('disabled', disabled);
            }
        },

        validate: function( $el ) {
            return edx.utils.validate( $el );
        }
    });

})(jQuery, _, Backbone, gettext);
