/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */

(function (root, factory) {
    'use strict';

    if (typeof define === 'function' && define.amd) {
        define([
            'jquery',
            'mage/template',
            'jquery/ui',
            'mage/translate-inline',
            'mage/translate'
        ], factory);
    } else {
        factory(root.jQuery, root.mageTemplate);
    }
}(this, function ($, mageTemplate) {
    'use strict';

    /**
     * Widget for a dialog to edit translations.
     */
    $.widget('mage.translateInlineDialogVde', $.mage.translateInline, {
        options: {
            translateForm: {
                template: '#translate-inline-dialog-form-template',
                data: {
                    selector: '#translate-inline-dialog-form'
                }
            },
            dialogClass: 'translate-dialog',
            draggable: false,
            modal: false,
            resizable: false,
            height: 'auto',
            minHeight: 0,
            buttons: [{
                text: $.mage.__('Cancel'),
                'class': 'translate-dialog-cancel',

                /**
                 * Click.
                 */
                click: function () {
                    $(this).translateInlineDialogVde('close');
                }
            },
            {
                text: $.mage.__('Save'),
                'class': 'translate-dialog-save',

                /**
                 * Click.
                 */
                click: function () {
                    $(this).translateInlineDialogVde('submit');
                }
            }],

            area: 'vde',
            ajaxUrl: null,
            textTranslations: null,
            imageTranslations: null,
            scriptTranslations: null,
            translateMode: null,
            translateModes: ['text', 'script', 'alt']
        },

        /**
         * Identifies if the form is already being submitted.
         *
         * @type {Boolean}
         */
        isSubmitting: false,

        /**
         * Identifies if inline text is being editied.  Only one element can be edited at a time.
         *
         * @type {Boolean}
         */
        isBeingEdited: false,

        /**
         * Creates the translation dialog widget. Fulfills jQuery WidgetFactory _create hook.
         */
        _create: function () {
            this._super();
            // Unbind previously bound events that may be present from previous loads of vde container.
            if (parent && parent.jQuery) {
                parent.jQuery('[data-frame="editor"]')
                    .off('modeChange')
                    .on('modeChange', $.proxy(this._checkTranslateEditing, this));
            }
        },

        /**
         * @param {jQuery.Event} e
         * @param {Object} widget
         * @param {Function} callback
         */
        openWithWidget: function (e, widget, callback) {
            if (widget && callback) {
                this.callback = callback;
                this.element.html(this._prepareContent($(widget.element).data('translate')));
                this.triggerElement = widget.element;
                $(window).on('resize.translateInlineDialogVde', $.proxy(this._positionNearTarget, this));
                this._positionNearTarget();
            }
            this.open(arguments);
        },

        /**
         * @private
         */
        _positionNearTarget: function () {
            this.option('position', {
                of: this.triggerElement,
                my: 'left top',
                at: 'left-3 top-9'
            });
            this.option('width', $(this.triggerElement).width());
        },

        /**
         * Close.
         */
        close: function () {
            this._super();
            this._onCancel();
            this.isBeingEdited = false;
            $(window).off('resize.translateInlineVdeDialog');
        },

        /**
         * Shows translate mode applicable css styles.
         */
        toggleStyle: function (mode) {
            // TODO: need to remove eventually
            this._toggleOutline(mode);

            this.options.textTranslations.translateInlineVde('toggleIcon', mode);
            this.options.imageTranslations.translateInlineImageVde('toggleIcon', mode);
            this.options.scriptTranslations.translateInlineScriptVde('toggleIcon', mode);
        },

        /**
         * Determine if user has modified inline translation text, but has not saved it.
         */
        _checkTranslateEditing: function (event, data) {
            var url, dataDisable;

            if (this.isBeingEdited) {
                alert(data['alert_message']); //eslint-disable-line no-alert
                data['is_being_edited'] = true;
            } else {
                // Disable inline translation.
                url = parent.jQuery('[data-frame="editor"]').attr('src');
                dataDisable = {
                    frameUrl: url.substring(0, url.indexOf('translation_mode')),
                    mode: this.options.translateMode
                };
                parent.jQuery('[vde-translate]').trigger('disableInlineTranslation', dataDisable);

                // Inline translation text is not being edited.  Continue on.
                parent.jQuery('[data-frame="editor"]').trigger(data['next_action'], data);
            }
        },

        /**
         * @return {*}
         * @private
         */
        _prepareContent: function () {
            var content = this._superApply(arguments);

            this._on(content.find('textarea[data-translate-input-index]'), {
                /**
                 * @param {jQuery.Event} e
                 */
                keydown: function (e) {
                    var keyCode = $.ui.keyCode;

                    switch (e.keyCode) {
                        case keyCode.ESCAPE:
                            e.preventDefault();
                            this.close();
                            break;

                        case keyCode.ENTER:
                            e.preventDefault();
                            this._formSubmit();
                            break;
                        default:
                            // keep track of the fact that translate text has been changed
                            this.isBeingEdited = true;
                    }
                }
            });
            this._on(content.find(this.options.translateForm.data.selector), {
                /**
                 * @param {jQuery.Event} e
                 * @return {Boolean}
                 */
                submit: function (e) {
                    e.preventDefault();
                    this._formSubmit();

                    return true;
                }
            });

            return content;
        },

        /**
         * Submits the form.
         */
        _formSubmit: function () {
            this._superApply(arguments);
            this.isBeingEdited = false;
        },

        /**
         * Callback for when the AJAX call in _formSubmit is completed.
         */
        _formSubmitComplete: function () {
            // TODO: need to replace with merged version
            var self = this;

            this.element.find('[data-translate-input-index]').each($.proxy(function (count, elem) {
                var index = $(elem).data('translate-input-index'),
                    value = $(elem).val() || '';

                self.callback(index, value);
                self = null;
            }, this));

            $(window).off('resize.translateInlineVdeDialog');
            this._onSubmitComplete();

            this._superApply(arguments);
            this.isSubmitting = false;
        },

        /**
         * @param {*} mode
         * @private
         */
        _toggleOutline: function (mode) {
            var that = this;

            // TODO: need to remove eventually
            if (mode == null) {
                mode = this.options.translateMode;
            } else {
                this.options.translateMode = mode;
            }

            this.element.closest('[data-container="body"]').addClass('trnslate-inline-' + mode + '-area');
            $.each(this.options.translateModes, function () {
                if (this != mode) { //eslint-disable-line eqeqeq
                    that.element.closest('[data-container="body"]').removeClass('trnslate-inline-' + this + '-area');
                }
            });
        },

        /**
         * @private
         */
        _onCancel: function () {
            // TODO: need to remove eventually
            this._toggleOutline();
            this.options.textTranslations.translateInlineVde('show');
            this.options.imageTranslations.translateInlineImageVde('show');
            this.options.scriptTranslations.translateInlineScriptVde('show');
        },

        /**
         * @private
         */
        _onSubmitComplete: function () {
            // TODO: need to remove eventually
            this._toggleOutline();
            this.options.textTranslations.translateInlineVde('show');
            this.options.imageTranslations.translateInlineImageVde('show');
            this.options.scriptTranslations.translateInlineScriptVde('show');
        }
    });

    /**
     * Widget for an icon to be displayed indicating that text can be translated.
     */
    $.widget('mage.translateInlineVde', {
        options: {
            iconTemplateSelector: '[data-template="translate-inline-icon"]',
            img: null,
            imgHover: null,

            offsetLeft: -16,

            dataAttrName: 'translate',
            translateMode: null,

            /**
             * On click.
             */
            onClick: function () {}
        },

        /**
         * Elements to wrap instead of just inserting a child element. This is
         * to work around some different behavior in Firefox vs. WebKit.
         *
         * @type {Array}
         */
        elementsToWrap: ['button'],

        /**
         * Determines if the template is already appended to the element.
         *
         * @type {Boolean}
         */
        isTemplateAttached: false,

        iconTemplate: null,
        iconWrapperTemplate: null,
        elementWrapperTemplate: null,

        /**
         * Determines if the element is suppose to be wrapped or just attached.
         *
         * @type {Boolean}, null is unset, false/true is set
         */
        isElementWrapped: null,

        /**
         * Creates the icon widget to indicate text that can be translated.
         * Fulfills jQuery's WidgetFactory _create hook.
         */
        _create: function () {
            this.element.addClass('translate-edit-icon-container');
            this._initTemplates();
            this.show();
        },

        /**
         * Shows the widget.
         */
        show: function () {
            this._attachIcon();

            this.iconTemplate.removeClass('hidden');

            if (this.element.data('translateMode') != this.options.translateMode) { //eslint-disable-line eqeqeq
                this.iconTemplate.addClass('hidden');
            }

            this.element.on('dblclick', $.proxy(this._invokeAction, this));
            this._disableElementClicks();
        },

        /**
         * Show edit icon for given translate mode.
         */
        toggleIcon: function (mode) {
            if (mode == this.element.data('translateMode')) { //eslint-disable-line eqeqeq
                this.iconTemplate.removeClass('hidden');
            } else {
                this.iconTemplate.addClass('hidden');
            }

            this.options.translateMode = mode;
        },

        /**
         * Determines if the element should have an icon element wrapped around it or
         * if an icon element should be added as a child element.
         */
        _shouldWrap: function () {
            var c;

            if (this.isElementWrapped !== null) {
                return this.isElementWrapped;
            }

            this.isElementWrapped = false;

            for (c = 0; c < this.elementsToWrap.length; c++) {
                if (this.element.is(this.elementsToWrap[c])) {
                    this.isElementWrapped = true;
                    break;
                }
            }

            return this.isElementWrapped;
        },

        /**
         * Attaches an icon to the widget's element.
         */
        _attachIcon: function () {
            if (this._shouldWrap()) {
                if (!this.isTemplateAttached) {
                    this.iconWrapperTemplate = this.iconTemplate.wrap('<div/>').parent();
                    this.iconWrapperTemplate.addClass('translate-edit-icon-wrapper-text');

                    this.elementWrapperTemplate = this.element.wrap('<div/>').parent();
                    this.elementWrapperTemplate.addClass('translate-edit-icon-container');

                    this.iconTemplate.appendTo(this.iconWrapperTemplate);
                    this.iconWrapperTemplate.appendTo(this.elementWrapperTemplate);
                }
            } else {
                this.iconTemplate.appendTo(this.element);
                this.element.removeClass('invisible');
            }

            this.isTemplateAttached = true;
        },

        /**
         * Disables the element click from actually performing a click.
         */
        _disableElementClicks: function () {
            this.element.find('a').off('click');

            if (this.element.is('A')) {
                this.element.on('click', function () {
                    return false;
                });
            }
        },

        /**
         * Hides the widget.
         */
        hide: function () {
            this.element.off('dblclick');
            this.iconTemplate.addClass('hidden');
        },

        /**
         * Replaces the translated text inside the widget with the new value.
         */
        replaceText: function (index, value) {
            var translateData = this.element.data(this.options.dataAttrName),
                innerHtmlStr = this.element.html();

            if (value === null || value === '') {
                value = '&nbsp;';
            }

            innerHtmlStr =  innerHtmlStr.replace(translateData[index].shown, value);

            this.element.html(innerHtmlStr);

            translateData[index].shown = value;
            translateData[index].translated = value;
            this.element.data(this.options.dataAttrName, translateData);
        },

        /**
         * Initializes all the templates for the widget.
         */
        _initTemplates: function () {
            this._initIconTemplate();
            this.iconTemplate.addClass('translate-edit-icon-text');
        },

        /**
         * Changes depending on hover action.
         */
        _hoverIcon: function () {
            if (this.options.imgHover) {
                this.iconTemplate.prop('src', this.options.imgHover);
            }
        },

        /**
         * Changes depending on hover action.
         */
        _unhoverIcon: function () {
            if (this.options.imgHover) {
                this.iconTemplate.prop('src', this.options.img);
            }
        },

        /**
         * Initializes the icon template for the widget. Sets the widget up to
         * respond to events.
         */
        _initIconTemplate: function () {
            this.iconTemplate = $(mageTemplate(this.options.iconTemplateSelector, {
                data: this.options
            }));

            this.iconTemplate.on('click', $.proxy(this._invokeAction, this))
                             .on('mouseover', $.proxy(this._hoverIcon, this))
                             .on('mouseout', $.proxy(this._unhoverIcon, this));
        },

        /**
         * Invokes the action (e.g. activate the inline dialog)
         */
        _invokeAction: function (event) {
            this._detachIcon();
            this.options.onClick(event, this);
        },

        /**
         * Destroys the widget. Fulfills jQuery's WidgetFactory _destroy hook.
         */
        _destroy: function () {
            this.iconTemplate.remove();
            this._detachIcon();
        },

        /**
         * Detaches an icon from the widget's element.
         */
        _detachIcon: function () {
            this._unhoverIcon();

            $(this.iconTemplate).detach();

            if (this._shouldWrap()) {
                this.iconWrapperTemplate.remove();
                this.element.unwrap();
                this.elementWrapperTemplate.remove();
            } else {
                this.element.addClass('invisible');
            }

            this.isTemplateAttached = false;
        }
    });

    $.widget('mage.translateInlineImageVde', $.mage.translateInlineVde, {
        /**
         * @private
         */
        _attachIcon: function () {
            if (!this.isTemplateAttached) {
                this.iconWrapperTemplate = this.iconTemplate.wrap('<div/>').parent();
                this.iconWrapperTemplate.addClass('translate-edit-icon-wrapper-image');

                this.elementWrapperTemplate = this.element.wrap('<div/>').parent();
                this.elementWrapperTemplate.addClass('translate-edit-icon-container');

                this.iconTemplate.appendTo(this.iconWrapperTemplate);
                this.iconWrapperTemplate.appendTo(this.elementWrapperTemplate);

                this.isTemplateAttached = true;
            }
        },

        /**
         * @private
         */
        _initTemplates: function () {
            this._initIconTemplate();
            this.iconTemplate.addClass('translate-edit-icon-image');
        },

        /**
         * @private
         */
        _detachIcon: function () {
            $(this.iconTemplate).detach();
            this.iconWrapperTemplate.remove();
            this.element.unwrap();
            this.elementWrapperTemplate.remove();

            this.isTemplateAttached = false;
        }
    });

    $.widget('mage.translateInlineScriptVde', $.mage.translateInlineVde, {});

    /*
     * @TODO move the "escapeHTML" method into the file with global utility functions
     */
    $.extend(true, $, {
        mage: {
            /**
             * @param {String} str
             * @return {String}
             */
            escapeHTML: function (str) {
                return str ? str.replace(/"/g, '&quot;') : '';
            }
        }
    });
}));
