/**
 * Creates an inline wysiwyg editor.
 *
 * The `ep.uiEditor` method creates an inline wysiwyg editor.
 *
 * ### Examples
 * Creates a wysiwyg editor depending on a textarea with id and name attributes. Textareas with the same value in the name attribute become a shared toolbar.
 *
 * JavaScript:
 *
 *     ep('#editorID').uiEditor({
 *       'name': 'editorName',
 *       'toolbar': 'Mini',
 *       'disable': true,
 *       'hideToolbarOnStartup': false,
 *       'noToolbar': true,
 *       'useAbsolute': true,
 *       'blogPostMode': true,
 *       'pluginOptions': {}
 *     });
 *
 * HTML:
 *
 *     <div style="clear:both; position:relative;">
 *       <textarea style="visiblity:hidden;" name="editorName" id="editorID">Some Content</textarea>
 *     </div>
 *
 *
 * @class jQuery.ui.uiEditor
 * @extends jQuery.widget
 *
 * @uses ep
 * @uses jQuery.ckeditor.adapters.jquery
 * @uses jQuery.event.special.dom
 * @since 6.15.0
 */

/**
 * Reloads the toolbar depending on the language of the current editor.
 *
 * @param {String} editorName Name of the editor handed over with the name option when editor widget gets initialized.
 *
 * @method reloadToolbar
 * @member jQuery.ui.uiEditor
 *
 * @since 6.15.0
 */

/**
 * @cfg {String} name Name of the editor (corresponds to the name attribute of the textarea).
 */

/**
 * @cfg {String} toolbar Indicates which toolbar should be used. If option is not handed over a default value from the config.js is used (depending on screen width, content etc.). Possible values: 'Full', 'FullPageBreak', 'Mini', 'MiniPageBreak'
 */

/**
 * @cfg {Boolean} disable Boolean Flag which indicates if the editor is disabled (true) on startup.
 */

/**
 * @cfg {Boolean} hideToolbarOnStartup Boolean Flag which indicates if the toolbar should be hidden (true) on startup.
 */

/**
 * @cfg {Boolean} noToolbar Boolean Flag which indicates if editor has a toolbar.
 */

/**
 * @cfg {Boolean} useAbsolute Boolean Flag which indicates if an absolute url should be used in the linkpicker plugin.
 */

/**
 * @cfg {Boolean} blogPostMode Boolean Flag which indicates if the toolbar should contain button to insert a pagebreak.
 */

/**
 * @cfg {Object} pluginOptions Additional options for plugins
 */

/**
 * See `jQuery.ui.uiEditor` for details.
 *
 * @param {Object} options A map of additional options pass to the method.
 * @param {String} name Name of the editor (corresponds to the name attribute of the textarea).
 * @param {String} toolbar Indicates which toolbar should be used. If option is not handed over a default value from the config.js is used (depending on screen width, content etc.). Possible values: 'Full', 'FullPageBreak', 'Mini', 'MiniPageBreak'
 * @param {Boolean} disable Boolean Flag which indicates if the editor is disabled (true) on startup.
 * @param {Boolean} hideToolbarOnStartup Boolean Flag which indicates if the toolbar should be hidden (true) on startup.
 * @param {Boolean} noToolbar Boolean Flag which indicates if editor has a toolbar.
 * @param {Boolean} useAbsolute Boolean Flag which indicates if an absolute url should be used in the linkpicker plugin.
 * @param {Boolean} blogPostMode Boolean Flag which indicates if the toolbar should contain button to insert a pagebreak.
 * @param {Object} pluginOptions Additional options for plugins
 *
 * @method uiEditor
 * @member jQuery
 *
 * @since 6.15.0
 */

/*
 * @copyright       © Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module          ep.ui.editor
 *
 * @revision        $Revision: 1.21 $
 */
/*global define, window*/
/*jslint vars:true, plusplus:true, nomen:true*/
define("ep/ui/editor", [
    "jquery",
    "ep",
    "ckeditor/ckeditor",

    "ep/ui/core",
    "ep/fn/busy",
    "jquery/ui/widget",
    "jquery/ckeditor",
    "jquery/event/special/dom"
], function ($, ep, CKEDITOR) {
    'use strict';
    var editorStack = {};
    /*  // example for an entry in editorStack
    {
        'Text' : {
            'editors' : [node1, ode2], //jQuery objects of editor nodes with this particular name
            'toolbar' : node,
            'leftEditor' : $Object,
            'rightEditor' : $Object,
            'disabledLayer': $Object
            }
    }
    */

    // recalculate toolbar width if window is resized
    $(window).on('resize.uiEditor', function () {
        var key, i;
        for (key in editorStack) {
            if (editorStack.hasOwnProperty(key)) {
                var editors = editorStack[key].editors;
                // iterate over "language" specific editors
                for (i = editors.length - 1; i >= 0; i--) {
                    ep(editors[i]).uiEditor('adjustToolbar', key);
                }
            }
        }
    });

    $.widget('ui.uiEditor', {
    //  editorInstance : undefined,
        baseFloatZIndex: 450,

        options: {
        //  name: undefined,
        //  toolbar: undefined,
            hideToolbarOnStartup: false,
            sourceMode: false,
            noToolbar : false,
            useAbsolute: false,
            disable: false,
            blogPostMode: false,
            showPreview: false,
            enterMode: 1 // is the same as CKEDITOR.ENTER_P
        },

        _create: function () {
            var self = this,
                o = self.options,
                elem = self.element,
                editorName = o.name = o.name || 'editor_' + new Date().getTime();

            // push editor element in stack
            if (editorStack[editorName]) {
                editorStack[editorName].editors.push(elem);
            } else {
                editorStack[editorName] = { 'editors' : [elem] };
                var toolbarContainer = ep('<div>')
                    .insertBefore(elem)
                    .attr('id', 'ep-toolbarContainer' + editorName)
                    .hide();

                editorStack[editorName].toolbar = toolbarContainer;
            }

            // if editor element is visible -> create editor instance immediately
            if (elem.is(':visible')) {
                self._createEditor();
                elem.parent().on('show', function () {
                    self.adjustToolbar(editorName);
                });
            } else {
                // else: create instance when editor element becomes visible
                elem.one('show', function () {
                    self._createEditor();
                    elem.parent().on('show', function () {
                        self.adjustToolbar(editorName);
                    });
                });
            }
        },

        // method to create editor instance
        _createEditor: function () {
            var self = this,
                o = self.options,
                elem = self.element,
                editorName = o.name,
                stack = editorStack[editorName],
                // additional configuration options for the editor which will be built below (2nd argument for the .ckeditor method)
                editorOptions = {
                    enterMode: o.enterMode,
                    sharedSpaces: {
                        top : 'ep-toolbarContainer' + editorName
                    }
                };

            // Pass options for plugins to the editor instance.
            if (o.pluginOptions) {
                $.extend(editorOptions, {pluginOptions: o.pluginOptions});
            }

            if (o.toolbar) {
                $.extend(editorOptions, {toolbar: o.toolbar});
            }
            // indicates whether to use a absolute/relative url in linkpicker widget/plugin
            if (o.useAbsolute) {
                $.extend(editorOptions, {useAbsolute: o.useAbsolute});
            }

            if (o.showPreview) {
                $.extend(editorOptions, {coreStyles_italic: { element : 'i', overrides : 'em' }});
            }

            var toolbarMode = 'Full';
            if (o.blogPostMode) {
                toolbarMode += 'PageBreak';
                $.extend(editorOptions, {blogPostMode: true});
            }
            if ($('#ep-toolbarContainer' + editorName).closest('tr').width() <= 1080) {
                toolbarMode = toolbarMode.replace('Full', 'Mini');
            }
            // After the first resize the help sidebar could be expanded and resize the editor width
            $(window).one('resize.uiEditor', function () {
                if ($('#ep-toolbarContainer' + editorName).closest('tr').width() <= 1080) {
                    toolbarMode = toolbarMode.replace('Full', 'Mini');
                    $.extend(editorOptions, {toolbar: toolbarMode});
                }
            });
            $.extend(editorOptions, {
                toolbar: toolbarMode,
                startupMode: (o.sourceMode) ? 'source' : 'wysiwyg'
            });

            elem.ckeditor(function (textarea) {
                // hide busy layer
                ep(elem.closest('div')).busy('hide');

                self.editorInstance = elem.ckeditorGet();
                self.baseFloatZIndex = self.editorInstance.config.baseFloatZIndex;

                var snapshotOnceEvent = function () {
                    elem.trigger('change');
                    self.editorInstance.removeListener('saveSnapshot', snapshotOnceEvent);
                };
                self.editorInstance.on('saveSnapshot', snapshotOnceEvent);

                //callback function after editor is ready
                var firstEditorNode =  $(stack.editors[0].ckeditorGet().container.$),
                    currentEditorNode = $(self.editorInstance.container.$),
                    leftEditor = firstEditorNode,
                    rightEditor = firstEditorNode,
                    i,
                    iLength,
                    doc = new CKEDITOR.dom.document(self.editorInstance.window.$.document),
                    editor = self.editorInstance;

                if (CKEDITOR.env.webkit) {
                    // Fix problem with cursor not appearing in Chrome when clicking below the body (#10945).
                    doc.getDocumentElement().on( 'mousedown', function( evt ) {
                            if (evt.data.getTarget().is( 'html' )){
                                editor.editable().focus();
                            }
                    } );
                }

                // iterate over "language" specific editors -> find the most left and most right editor
                for (i = 0, iLength = stack.editors.length; i < iLength; i++) {
                    var editorNode = $(stack.editors[i].ckeditorGet().container.$),
                        offsetLeft = editorNode.offset().left;

                    if (offsetLeft < leftEditor.offset().left) {
                        leftEditor = editorNode;
                    } else if (offsetLeft > rightEditor.offset().left) {
                        rightEditor = editorNode;
                    }
                }
                stack.leftEditor = leftEditor;
                stack.rightEditor = rightEditor;

                // calculate toolbar width
                self.adjustToolbar(editorName, true);

                // if toolbar should not be shown on start up
                if (o.hideToolbarOnStartup) {
                    self.editorInstance.focusManager.focus = function () {
                        self.toggleToolbar(true);
                        self.editorInstance.fire('focus');
                    };
                    self.editorInstance.focusManager.blur = function () {
                        self.toggleToolbar(false);
                    };
                    self.toggleToolbar(false);
                }

                // trigger window resize to adjust toolbars of all created editors
                $(window).trigger('resize.uiEditor', [true]);

                // update corresponding textarea when losing focus
                self.editorInstance.on('blur', function () {
                    self.editorInstance.updateElement();
                });

                // if disable layer is undefined -> append new one to element and add to stack
                if (stack.disabledLayer === undefined) {
                    stack.disabledLayer = $("<div />");
                    stack.disabledLayer.insertBefore(stack.toolbar);
                }

                // style disable layer (element lies over editor and toolbar => both cannot be used)
                stack.disabledLayer
                    .addClass('cke_full_disabled')
                    .css({
//                      position: 'absolute',
//                      top: 0,
//                      left: 0,
//                      opacity: 0.7,
//                      backgroundColor: '#fff',
                        zIndex: stack.toolbar.css('z-index') + 1,
                        display: 'none',
                        width: stack.toolbar.width() + "px",
                        height: (stack.toolbar.height() + $(self.editorInstance.container.$).height()) + "px"
                    });

                //
                // In case we never display a toolbar and the editor is disabled, we go into *AlmostFullscreen*
                // mode for editing on click onto the *disabledLayer*.
                //
                if (o.disable && o.hideToolbarOnStartup && o.noToolbar) {
                    stack.disabledLayer.on('click', $.proxy(function (event) {
                        this.execCommand('epAlmostFullscreen');
                    }, self.editorInstance));
                }

                // remove title attributes from dropdowns (do not show obvious tooltips)
                stack.toolbar.find('.cke_rcombo a').removeAttr('title');

                // set initial state for disable layer
                self.disableEditor(o.disable);

                // to disable the whole editor (e.g. for not editable properties in a multistore subshop)
                if(elem.hasClass('Disabled') && elem.prop('disabled')) {
                    self.disableEditor(true);
                }

                $(window).trigger('scroll');
            }, editorOptions);
        },

        // show/hide disable layer
        disableEditor: function (disable) {
            editorStack[this.options.name].disabledLayer.css('display', (disable ? 'block' : 'none'));
        },

        // calculate new width of toolbar and resize it
        adjustToolbar: function (editorName, initial) {
            var stack = editorStack[editorName],
                self = this,
                elem = self.element;

            if (!stack.rightEditor || !stack.leftEditor) {
                return;
            }
            var oldToolbarHeight = stack.toolbar.height(),
                toolbarWidth = (stack.rightEditor.outerWidth(true) + stack.rightEditor.offset().left) - stack.leftEditor.offset().left;

            // render toolbar with new values if toolbar flag is set
            if (!self.options.noToolbar) {
                ep(stack.toolbar).css({
                    'width': toolbarWidth + 'px',
                    'z-index': 1,
                    display: ''
                });
            }

            // calculate new toolbar height
            var newToolbarHeight = stack.toolbar.is(':visible') ? stack.toolbar.height() : 0,
                currentEditorNode = $(elem.ckeditorGet().container.$),
                i,
                iLength;

            // if height has changed or it is the first call -> set top padding
            if (oldToolbarHeight !== newToolbarHeight || initial) {
                for (i = 0, iLength = stack.editors.length; i < iLength; i++) {
                    $(stack.editors[i].ckeditorGet().container.$).parent().css({
                        'paddingTop' : newToolbarHeight + 'px'
                    });
                }
            }

            $(stack.toolbar).css({
                'position': 'absolute',
                'margin-top' : '-' + newToolbarHeight + 'px'
            });

            // set dimensions for disable layer
            if (stack.disabledLayer) {
                stack.disabledLayer
                    .width(toolbarWidth)
                    .height(newToolbarHeight + $(elem.ckeditorGet().container.$).height());
            }

        },

        toggleToolbar: function (show) {
            var self = this,
                o = self.options;

            // move in another threat to ensure that hide/show is really called
            window.setTimeout(function () {
                editorStack[o.name].toolbar[show ? 'show' : 'hide']();
            }, 1);
            self.adjustToolbar(o.name, true);

        },

        destroy: function () {
            var self = this,
                o = self.options,
                name = o.name,
                destroy = true,
                stack = editorStack[name],
                key;

            self._superApply(arguments);

            // check if all similar editor instances are undefined
            for (key in stack.editors) {
                if (stack.editors.hasOwnProperty(key)) {
                    if (stack.editors[key].data('uiUiEditor') !== undefined) {
                        destroy = false;
                    }
                }
            }

            // if all similar editor instances are undefined
            if (destroy) {
                // destroy all instances
                for (key in stack.editors) {
                    if (stack.editors.hasOwnProperty(key)) {
                        CKEDITOR.instances[stack.editors[key].attr('id')].destroy();
                    }
                }
                // remove according toolbar
                stack.toolbar.remove();
                delete editorStack[name];
            }
        }
    });

    return ep;

});