define(["backbone", "underscore", "jquery", "gettext", "js/views/feedback_notification", "js/views/feedback_alert", "jquery.smoothScroll"],
    function(Backbone, _, $, gettext, NotificationView, AlertView) {

var ValidatingView = Backbone.View.extend({
    // Intended as an abstract class which catches validation errors on the model and
    // decorates the fields. Needs wiring per class, but this initialization shows how
    // either have your init call this one or copy the contents
    initialize : function() {
        this.listenTo(this.model, 'invalid', this.handleValidationError);
        this.selectorToField = _.invert(this.fieldToSelectorMap);
    },

    errorTemplate : _.template('<span class="message-error"><%= message %></span>'),

    save_title: gettext("You've made some changes"),
    save_message: gettext("Your changes will not take effect until you save your progress."),
    error_title: gettext("You've made some changes, but there are some errors"),
    error_message: gettext("Please address the errors on this page first, and then save your progress."),

    events : {
        "change input" : "clearValidationErrors",
        "change textarea" : "clearValidationErrors"
    },
    fieldToSelectorMap : {
        // Your subclass must populate this w/ all of the model keys and dom selectors
        // which may be the subjects of validation errors
    },
    _cacheValidationErrors : [],

    handleValidationError : function(model, error) {
        this.clearValidationErrors();
        // error is object w/ fields and error strings
        for (var field in error) {
            var ele = this.$el.find('#' + this.fieldToSelectorMap[field]);
            this._cacheValidationErrors.push(ele);
            this.getInputElements(ele).addClass('error');
            $(ele).parent().append(this.errorTemplate({message : error[field]}));
        }
        $('.wrapper-notification-warning').addClass('wrapper-notification-warning-w-errors');
        $('.action-save').addClass('is-disabled');
        // TODO: (pfogg) should this text fade in/out on change?
        $('#notification-warning-title').text(this.error_title);
        $('#notification-warning-description').text(this.error_message);
    },

    clearValidationErrors : function() {
        // error is object w/ fields and error strings
        while (this._cacheValidationErrors.length > 0) {
            var ele = this._cacheValidationErrors.pop();
            this.getInputElements(ele).removeClass('error');
            $(ele).nextAll('.message-error').remove();
        }
        $('.wrapper-notification-warning').removeClass('wrapper-notification-warning-w-errors');
        $('.action-save').removeClass('is-disabled');
        $('#notification-warning-title').text(this.save_title);
        $('#notification-warning-description').text(this.save_message);
    },

    setField : function(event) {
        // Set model field and return the new value.
        this.clearValidationErrors();
        var field = this.selectorToField[event.currentTarget.id];
        var newVal = $(event.currentTarget).val();
        this.model.set(field, newVal);
        this.model.isValid();
        return newVal;
    },
    // these should perhaps go into a superclass but lack of event hash inheritance demotivates me
    inputFocus : function(event) {
        $("label[for='" + event.currentTarget.id + "']").addClass("is-focused");
    },
    inputUnfocus : function(event) {
        $("label[for='" + event.currentTarget.id + "']").removeClass("is-focused");
    },

    getInputElements: function(ele) {
        var inputElements = 'input, textarea';
        if ($(ele).is(inputElements)) {
            return $(ele);
        }
        else {
            // put error on the contained inputs
            return $(ele).find(inputElements);
        }
    },

    showNotificationBar: function(message, primaryClick, secondaryClick) {
        // Show a notification with message. primaryClick is called on
        // pressing the save button, and secondaryClick (if it's
        // passed, which it may not be) will be called on
        // cancel. Takes care of hiding the notification bar at the
        // appropriate times.
        if(this.notificationBarShowing) {
            return;
        }
        // If we've already saved something, hide the alert.
        if(this.saved) {
            this.saved.hide();
        }
        var self = this;
        this.confirmation = new NotificationView.Warning({
            title: this.save_title,
            message: message,
            actions: {
                primary: {
                    "text": gettext("Save Changes"),
                    "class": "action-save",
                    "click": function() {
                        primaryClick();
                        self.confirmation.hide();
                        self.notificationBarShowing = false;
                    }
                },
                secondary: [{
                    "text": gettext("Cancel"),
                    "class": "action-cancel",
                    "click": function() {
                        if(secondaryClick) {
                            secondaryClick();
                        }
                        self.model.clear({silent : true});
                        self.confirmation.hide();
                        self.notificationBarShowing = false;
                    }
                }]
            }});
        this.notificationBarShowing = true;
        this.confirmation.show();
        // Make sure the bar is in the right state
        this.model.isValid();
    },

    showSavedBar: function(title, message) {
        var defaultTitle = gettext('Your changes have been saved.');
        this.saved = new AlertView.Confirmation({
            title: title || defaultTitle,
            message: message,
            closeIcon: false
        });
        this.saved.show();
        $.smoothScroll({
            offset: 0,
            easing: 'swing',
            speed: 1000
        });
    },

    saveView: function() {
        var self = this;
        this.model.save(
            {},
            {
                success: function() {
                    self.showSavedBar();
                },
                silent: true
            }
        );
    }
});

return ValidatingView;

}); // end define()
