/*globals define, document*/
/*jslint nomen:true*/

/**
 * Advanced handling for input elements.
 *
 * The `.uiInput()` method add advanced handling to a input elements.
 *
 * ### Examples
 * Apply .uiInput() to all input element and set autofocus to the first matched element.
 *
 * JavaScript:
 *
 *     ep(':input')
 *         .first()
 *             .uiInput({autofocus: true})
 *         .end()
 *         .not(':first')
 *             .uiInput()
 *         .end();
 *
 *
 * @class jQuery.ui.uiInput
 * @extends jQuery.widget
 *
 * @uses ep
 * @uses ep.dict
 * @uses jQuery.support.placeholder
 * @uses jQuery.support.multipleUpload
 * @uses jQuery.fn.form
 * @uses jQuery.fn.class
 * @uses ep.ui.tooltip
 * @since 6.11.0
 */

/**
 * See `jQuery.ui.uiInput` for details.
 *
 * @fires changeValue
 *
 * @method uiInput
 * @member jQuery
 *
 * @since 6.11.0
 */

/*
 * @copyright       © Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module          ep.ui.input
 */

define("ep/ui/input", [
    "jquery",
    "ep",
    "util/support",
    "$dict!ep/dict",

    "jquery/fn/form",
    "jquery/fn/class",
    "ep/ui/tooltip"
], function ($, ep, support, epDict) {
    'use strict';

    /*
     * @dictionary      ep.dict
     *
     * @translation     {SelectFile}
     */


    /**
     * @event changeValue This event is triggered when the value is changed.
     * @member jQuery.ui.uiInput
     * @since 6.11.0
     */
    $(document)
        .on("click", "input + .ep-uiInput-custom:not(label *, input:disabled + *)", function (event) {
            // Trigger click from custom ui element on original one
            $(this.previousSibling)
                .trigger({
                    type: "click",
                    data: event.data,
                    originalTarget: this.previousSibling
                });
        });

    $.widget('ui.uiInput', {

        /**
         * @cfg {Object}    options                 Option to pass to the widget.
         * @cfg {String}    options.placeholder     Placeholder text.
         * @cfg {String}    options.info            An info text show as tooltip if the element has focus.
         * @cfg {Boolean}   options.autofocus=false Indication whether the element gets focus on page load.
         * @cfg {Boolean}   options.big=false       Indication whether the element is displayed bigger than the normal one.
         * @cfg {String}    options.orientation     Decide in which direction tooltipps will be shown. Can be either *right*, *left*, *top* or *bottom*
         */
        options: {
            //  placeholder:        'text',
            //  info:               'text',
            autofocus: false,
            big: false
        },

        _create: function () {
            var self = this,
                o = self.options,

                elem = self.elem = self.element
                    .data('uiUiInput', self),

                // stack of additional elements
                stack = self.stack = $([]),

                // get type
                type = self.type = elem[0].tagName.toLowerCase() + ':',

                // classes
                elemClasses = '';

            // detailed type
            self.type = type = (type === 'input:' || type === 'button:') ? type + elem[0].type : type;

            // get instance of dict
            self.dict = epDict;

            // set placeholder
            if (o.placeholder) {
                elem.attr('placeholder', o.placeholder);
            }

            elem.on('changeAttr.uiInput', $.proxy(self, '_changeAttr'))
                .on('reset.uiInput', $.proxy(self, '_change'))
                .on('focusin.uiInput', function () {
                    if (self.tooltip) {
                        self.tooltip[self.tooltip.is(':empty') ? 'hide' : 'show']();
                    }
                })
                .on('focusout.uiInput', function () {
                    self._leave();
                    if (self.tooltip) {
                        self.tooltip.hide();
                    }
                });

            if ((/file$/i).test(type)) {
                elemClasses += ' ep-uiInput-file';
                // create a button
                stack.push(
                    (
                        self.custom = $('<span class="ep-uiInput-custom"><span class="ep-uiInput ep-uiInput-button">' + self.dict.translate('SelectFile') + '...</span></span>')
                        .insertAfter(elem)
                    )[0]
                );
                // create a input to display selected file name
                stack.push(
                    (
                        self.fileText = $('<span class="ep-uiInput ep-uiInput-text">' + (elem.val() || '&nbsp;') + '</span>')
                        .prependTo(self.custom)
                    )[0]
                );
            }

            if (self.options.big) {
                elemClasses += ' ep-uiInput-big';
            }
            if ((/(^(textarea:|select:)|(text|password|email|url|tel|date|datetime|time|number)$)/).test(type)) {
                elemClasses += ' ep-uiInput';
            }

            if ((/^select:/).test(type)) {
                elemClasses += ' ep-uiInput-select';
            } else if ((/(^(button:|a:)|(submit|reset|button|image)$)/).test(type)) {
                elemClasses += (/image$/).test(type) ? ' ep-uiInput ep-uiInput-image' : ' ep-uiInput ep-uiInput-button';
                // handle reset on form
                if ((/reset$/).test(type)) {
                    elem.on('click.uiInput', function (event) {
                        event.preventDefault();
                        $(this.form).formReset();
                    });
                }
                // handle submit on buttons having the form attribute
                if ((/submit$/).test(type)) {
                    if (this.elem.attr('form')) {
                        if (!support.inputFormAttr) {
//#JSCOVERAGE_IF false
// not testable because HTML5 compatability cannot be faked
                            this.elem.on('click', function (event) {

                                if (event.preventDefault) {
                                    event.preventDefault();
                                } else {
                                    event.returnValue = false;
                                }

                                $('form#' + $(this).attr('form')).submit();
                            });
//#JSCOVERAGE_ENDIF
                        }
                    }
                }
            } else if ((/^textarea:$/).test(type)) {
                elemClasses += ' ep-uiInput-textarea2';
            } else if ((/(text|password|email|url|tel|date|datetime|time|number)$/i).test(type)) {
                elemClasses += ' ep-uiInput-text';
            }
            // change event
            if ((/(^(select:)|(file|checkbox|radio)$)/).test(type)) {
                elem.on('change.uiInput', $.proxy(self, '_change'));
            }
            // key event
            if ((/(^(textarea:)|(text|password|checkbox|radio|email|url|tel|date|datetime|time|number)$)/).test(type)) {
                elem.on('keyup.uiInput', $.proxy(self, '_change'));
            }

            // handle z-index of input[text]
            // if ((/(text|password|email|url|tel|date|datetime|time|number)$/i).test(type)) {
            if ((/text$/).test(type)) {
                elem.on('mouseup.uiInput', function (event) {
                    if (self.tooltip) {
                        // force focus for text input
                        // (Firefox bug with tooltip that compete with the input about the z-index => input loses the focus and is not clickable)
                        window.setTimeout(function() {
                            elem.trigger('focus');
                        }, 0);
                    }
                });
            }

            // add classes for styling
            elem.addClass(elemClasses);

            // save value state
            self.lastState = [elem[0].value, elem[0].checked, elem[0].selectedIndex];
        },

        // init binds
        _init: function () {

            // init tooltip
            this._tooltipInit();

            // handle autofocus for this elem
            if (this.elem.attr('autofocus') || this.options.autofocus) {
                this.elem[0].focus();
            }
        },

        // handle addClass to the wrap
        /**
         * The `.uiInput()` method wraps an element around the input, the addClass add classes to the wrap element.
         *
         * @param {String} className One or more class names to be added to the class attribute of each matched element.
         *
         * @method addClass
         * @member jQuery.ui.uiInput
         *
         * @since 6.11.0
         */
        addClass: function (name) {
            if (!(/(hover|active|focused)/).test(name) || !this.elem.is(':disabled,:readonly')) {
                this.elem.addClass(name);
            }
        },

        // handle removeClass to the wrap
        /**
         * The `.uiInput()` method wraps an element around the input, the removeClass removes classes from the wrap element.
         *
         * @param {String} className A class name to be removed from the class attribute of each matched element.
         *
         * @method removeClass
         * @member jQuery.ui.uiInput
         *
         * @since 6.11.0
         */
        removeClass: function (name) {
            this.elem.removeClass(name);
        },

        _tooltipInit: function (buildManual) {
            if (!this.tooltip && (this.options.info || buildManual)) {
                this.tooltip = ep('<div>')
                    .addClass('ep-uiInput-info')
                    .uiTooltip({
                        interactive: true,
                        event: 'focus',
                        context: this.elem,
                        orientation: this.options.orientation || 'right',
                        offsetAdjust: [0, 0]
                    });

                if (this.options.info) {
                    this.tooltip.html(this.options.info);
                }

                // AD-2185 changes
                if (this.options.orientation === 'bottom' && this.tooltip !== undefined && $(this.elem).outerWidth() > 100) {
                    $(this.tooltip).parent().css('width', $(this.elem).outerWidth());
                }

                // add to stack
                this.stack.push(this.tooltip[0]);
            }
        },

        _setOption: function (key, value) {
            if (key === 'placeholder') {
                this.elem.attr('placeholder', value);
            }

            return this._superApply(arguments);
        },

        // actions if change attr by javascript
        _changeAttr: function (event, attr) {
            if (attr.hasOwnProperty('value')) {
                this._change({});
            }
        },

        _stateChanged: function () {
            var oldState = this.lastState,
                newState = [this.elem[0].value, this.elem[0].checked, this.elem[0].selectedIndex],
                ret = !(oldState[0] === newState[0] && oldState[1] === newState[1] && oldState[2] === newState[2]);

            this.lastState = newState;

            return ret;
        },

        _change: function (event) {
            if (event.keyCode !== 9) {
                // special for checkbox
                if ((/checkbox$/i).test(this.type)) {
                    this.elem.trigger('changeAttr', {
                        'checked': this.elem.is(':checked')
                    });
                } else if ((/radio$/i).test(this.type)) {
                    // special for radios
                    this.elem
                        .formGroup(':radio')
                        .filter(':checked')
                        .trigger('changeAttr', {
                            'checked': false
                        })
                        .end()
                        .filter(':not(:checked)')
                        .trigger('changeAttr', {
                            'checked': true
                        });
                } else if ((/file$/i).test(this.type)) {
                    // display selected file
                    if (this.elem.val() !== "") {
                        this.fileText.text(this.elem.val().replace(/^.*?fakepath[\/\\]?/, ''));
                    } else {
                        this.fileText.html('&nbsp;');
                    }
                }
            }

            this._leave();
        },

        _leave: function () {
            this.elem.toggleClass('ui-changed', this.elem.is(':changed'));

            if (this._stateChanged()) {
                this.elem.trigger('changeValue');
                // trigger changed to form
                $(this.elem[0].form).trigger('change');
            }

            // AD-2185 changes
            if (this.options.orientation === 'bottom' && this.tooltip !== undefined && $(this.elem).outerWidth() > 0) {
                $(this.tooltip).parent().css('width', $(this.elem).outerWidth());
            }
        },

        destroy: function () {
            this.elem
                .removeClass('ep-uiInput')
                .removeClass(/(ep-uiInput[\w\d-]*|epWidth-\d+)/g);
            this.stack.remove();

            return this._superApply(arguments);
        }
    });

    return ep;

});