(function(define, undefined) {
    'use strict';
    define([
        'jquery', 'underscore', 'annotator_1.2.9', 'js/edxnotes/utils/utils'
    ], function($, _, Annotator, Utils) {
        var _t = Annotator._t;

    /**
     * The original _setupDynamicStyle uses a very expensive call to
     * Util.maxZIndex(...) that sets the z-index of .annotator-adder,
     * .annotator-outer, .annotator-notice, .annotator-filter. We set these
     * values in annotator.min.css instead and do nothing here.
     */
        Annotator.prototype._setupDynamicStyle = function() { };

        Annotator.frozenSrc = null;

    /**
     * Modifies Annotator.Plugin.Auth.haveValidToken to make it work with a new
     * token format.
     **/
        Annotator.Plugin.Auth.prototype.haveValidToken = function() {
            return (
            this._unsafeToken &&
            this._unsafeToken.sub &&
            this._unsafeToken.exp &&
            this._unsafeToken.iat &&
            this.timeToExpiry() > 0
            );
        };

    /**
     * Modifies Annotator.Plugin.Auth.timeToExpiry to make it work with a new
     * token format.
     **/
        Annotator.Plugin.Auth.prototype.timeToExpiry = function() {
            var now = new Date().getTime() / 1000,
                expiry = this._unsafeToken.exp,
                timeToExpiry = expiry - now;

            return (timeToExpiry > 0) ? timeToExpiry : 0;
        };


        Annotator.Plugin.Tags.prototype.updateField = _.compose(
        function() {
            // Add screen reader label for edit mode. Note that the id of the tags element will not always be "1".
            // It depends on the number of annotatable components on the page.
            var tagsField = $('li.annotator-item >input', this.annotator.editor.element).attr('id');
            if ($("label.sr[for='" + tagsField + "']", this.annotator.editor.element).length === 0) {
                $('<label class="sr" for=' + tagsField + '>' + _t('Tags (space-separated)') + '</label>').insertBefore(
                    $('#' + tagsField, this.annotator.editor.element)
                );
            }
            return this;
        },
        Annotator.Plugin.Tags.prototype.updateField
    );

        Annotator.Plugin.Tags.prototype.updateViewer = _.compose(
        function() {
            // Add ARIA information for viewing mode.
            $('div.annotator-tags', this.wrapper).attr({
                role: 'region',
                'aria-label': 'tags'
            });
            return this;
        },
        Annotator.Plugin.Tags.prototype.updateViewer
    );

    /**
     * Modifies Annotator.highlightRange to add "tabindex=0" and role="link"
     * attributes to the <span class="annotator-hl"> markup that encloses the
     * note. These are then focusable via the TAB key and are accessible to
     * screen readers.
     **/
        Annotator.prototype.highlightRange = _.compose(
        function(results) {
            $('.annotator-hl', this.wrapper).attr({
                tabindex: 0,
                role: 'link'
            });
            return results;
        },
        Annotator.prototype.highlightRange
    );

    /**
     * Modifies Annotator.destroy to unbind click.edxnotes:freeze from the
     * document and reset isFrozen to default value, false.
     **/
        Annotator.prototype.destroy = _.compose(
        Annotator.prototype.destroy,
        function() {
            // We are destroying the instance that has the popup visible, revert to default,
            // unfreeze all instances and set their isFrozen to false
            if (this === Annotator.frozenSrc) {
                this.unfreezeAll();
            } else {
                // Unfreeze only this instance and unbound associated 'click.edxnotes:freeze' handler
                $(document).off('click.edxnotes:freeze' + this.uid);
                this.isFrozen = false;
            }

            if (this.logger && this.logger.destroy) {
                this.logger.destroy();
            }
            // Unbind onNoteClick from click
            this.viewer.element.off('click', this.onNoteClick);
            this.wrapper.off('click', '.annotator-hl');
        }
    );

    /**
     * Modifies Annotator.Viewer.html template to make viewer div focusable.
     * Also adds a close button and necessary i18n attributes to all buttons.
    **/
        Annotator.Viewer.prototype.html = {
            element: [
                '<div class="annotator-outer annotator-viewer">',
                '<ul class="annotator-widget annotator-listing" tabindex="-1"></ul>',
                '</div>'
            ].join(''),
            item: [
                '<li class="annotator-annotation annotator-item">',
                '<span class="annotator-controls">',
                '<a href="#" title="', _t('View as webpage'), '" class="annotator-link">',
                _t('View as webpage'),
                '</a>',
                '<button class="annotator-edit">',
                _t('Edit'),
                '<span class="sr">', _t('Note'), '</span>',
                '</button>',
                '<button class="annotator-delete">',
                _t('Delete'),
                '<span class="sr">', _t('Note'), '</span>',
                '</button>',
                '<button class="annotator-close">',
                _t('Close'),
                '<span class="sr">', _t('Note'), '</span>',
                '</button>',
                '</span>',
                '</li>'
            ].join('')
        };

    /**
     * Overrides Annotator._setupViewer to add a "click" event on viewer and to
     * improve line breaks.
     **/
        Annotator.prototype._setupViewer = function() {
            var self = this;
            this.viewer = new Annotator.Viewer({readOnly: this.options.readOnly});
            this.viewer.element.on('click', _.bind(this.onNoteClick, this));
            this.viewer.hide()
            .on('edit', this.onEditAnnotation)
            .on('delete', this.onDeleteAnnotation)
            .addField({
                load: function(field, annotation) {
                    if (annotation.text) {
                        $(field).html(Utils.nl2br(Annotator.Util.escape(annotation.text)));
                    } else {
                        $(field).html('<i>' + _t('No Comment') + '</i>');
                    }
                    return self.publish('annotationViewerTextField', [field, annotation]);
                }
            })
            .element.appendTo(this.wrapper).bind({
                mouseover: this.clearViewerHideTimer,
                mouseout: this.startViewerHideTimer
            });
            return this;
        };

        Annotator.Editor.prototype.isShown = Annotator.Viewer.prototype.isShown;

    /**
     * Modifies Annotator.Editor.html template to add tabindex = -1 to
     * form.annotator-widget and reverse order of Save and Cancel buttons.
     **/
        Annotator.Editor.prototype.html = [
            '<div class="annotator-outer annotator-editor">',
            '<form class="annotator-widget" tabindex="-1">',
            '<ul class="annotator-listing"></ul>',
            '<div class="annotator-controls">',
            '<button class="annotator-save">',
            _t('Save'),
            '<span class="sr">', _t('Note'), '</span>',
            '</button>',
            '<button class="annotator-cancel">',
            _t('Cancel'),
            '<span class="sr">', _t('Note'), '</span>',
            '</button>',
            '</div>',
            '</form>',
            '</div>'
        ].join('');


    /**
     * Modifies Annotator.Editor.show, in the case of a keydown event, to remove
     * focus from Save button and put it on form.annotator-widget instead.
     *
     * Also add a sr label for note textarea.
     **/
        Annotator.Editor.prototype.show = _.compose(
        function(event) {
            // Add screen reader label for the note area. Note that the id of the tags element will not always be "0".
            // It depends on the number of annotatable components on the page.
            var noteField = $('li.annotator-item >textarea', this.element).attr('id');
            if ($("label.sr[for='" + noteField + "']", this.element).length === 0) {
                $('<label class="sr" for=' + noteField + '>' + _t('Note') + '</label>').insertBefore(
                    $('#' + noteField, this.element)
                );
            }

            if (event.type === 'keydown') {
                this.element.find('.annotator-save').removeClass(this.classes.focus);
                this.element.find('form.annotator-widget').focus();
            }
        },
        Annotator.Editor.prototype.show
    );

    /**
     * Removes the textarea keydown event handler as it triggers 'processKeypress'
     * which hides the viewer on ESC and saves on ENTER. We will define different
     * behaviors for these in /plugins/accessibility.js
     **/
        delete Annotator.Editor.prototype.events['textarea keydown'];

    /**
     * Modifies Annotator.onHighlightMouseover to avoid showing the viewer if the
     * editor is opened.
     **/
        Annotator.prototype.onHighlightMouseover = _.wrap(
        Annotator.prototype.onHighlightMouseover,
        function(func, event) {
            // Do nothing if the editor is opened.
            if (this.editor.isShown()) {
                return false;
            }
            return func.call(this, event);
        },
        Annotator.prototype._setupViewer
    );

    /**
     * Modifies Annotator._setupWrapper to add a "click" event on '.annotator-hl'.
     **/
        Annotator.prototype._setupWrapper = _.compose(
        function() {
            this.element.on('click', '.annotator-hl', _.bind(this.onHighlightClick, this));
            return this;
        },
        Annotator.prototype._setupWrapper
    );

        $.extend(true, Annotator.prototype, {
            isFrozen: false,
            uid: _.uniqueId(),

            onHighlightClick: function(event) {
                event.stopPropagation();
                if (!this.editor.isShown()) {
                    this.unfreezeAll();
                    this.onHighlightMouseover.call(this, event);
                    Annotator.frozenSrc = this;
                    this.freezeAll();
                }
            },

            onNoteClick: function(event) {
                var $target = $(event.target);
                event.stopPropagation();
                Annotator.Util.preventEventDefault(event);

                if (!($target.is('.annotator-delete') || $target.is('.annotator-close'))) {
                    Annotator.frozenSrc = this;
                    this.freezeAll();
                } else if ($target.is('.annotator-close')) {
                    this.viewer.hide();
                }
            },

            freeze: function() {
                if (!this.isFrozen) {
                // Remove default events
                    this.removeEvents();
                    this.viewer.element.unbind('mouseover mouseout');
                    this.uid = _.uniqueId();
                    $(document).on('click.edxnotes:freeze' + this.uid, _.bind(this.unfreeze, this));
                    this.isFrozen = true;
                }
                return this;
            },

            unfreeze: function() {
                if (this.isFrozen) {
                // Add default events
                    this.addEvents();
                    this.viewer.element.bind({
                        mouseover: this.clearViewerHideTimer,
                        mouseout: this.startViewerHideTimer
                    });
                    this.viewer.hide();
                    $(document).off('click.edxnotes:freeze' + this.uid);
                    this.isFrozen = false;
                    Annotator.frozenSrc = null;
                }
                return this;
            },

            freezeAll: function() {
                _.invoke(Annotator._instances, 'freeze');
                return this;
            },

            unfreezeAll: function() {
                _.invoke(Annotator._instances, 'unfreeze');
                return this;
            },

            showFrozenViewer: function(annotations, location) {
                this.showViewer(annotations, location);
                this.freezeAll();
                return this;
            }
        });
    });
}).call(this, define || RequireJS.define);
