/**
 * Add spinner handling for input elements.
 *
 * The `.uiSpinner()` method add spinner handling to an input elements.
 *
 * The `.uiSpinner()` method is a validation input also and using the validation types ''date'' and ''number'' from
 * the ep.validate module. Have a look at the additional options, some options are only available for
 * a special validation types.
 *
 * ### Examples
 * Apply .uiSpinner() to input element named percent.
 *
 * JavaScript:
 *
 *     ep(':input[name=percent]')
 *         .uiSpinner({
 *             required:   true,
 *             type:       'number',
 *             format:     'p',
 *             min:        10,
 *             max:        50,
 *             step:       10
 *         });
 *
 * HTML:
 *
 *     <form>
 *       <input type="text" name="percent" />
 *     </form>
 *
 *
 * @class jQuery.ui.uiSpinner
 * @extends jQuery.ui.uiValidate
 *
 * @uses ep.ui.validate
 * @uses ep.date
 * @uses ep.sprite
 * @since 6.11.0
 */

/**
 * @cfg {String} type A value respectively validation type: 'number' or 'date'.
 */

/**
 * @cfg {String} unit A unit for example 'GByte', es this option only for type 'number' an format 'n*'
 */

/**
 * @cfg {Number} step A difference to next/previous step.
 */

/**
 * @cfg {Number} stepType One of the supported types: 'fullYear', 'month', 'date', 'hours', 'minutes' or 'seconds'. For spinner type date only.
 */

/**
 * See `jQuery.ui.uiSpinner` for details.
 *
 * @param {Object} [options] A map of additional options pass to the method.
 * @param {String} type A value respectively validation type: 'number' or 'date'.
 * @param {String} unit A unit for example 'GByte', es this option only for type 'number' an format 'n*'
 * @param {Number} step A difference to next/previous step.
 * @param {Number} stepType One of the supported types: 'fullYear', 'month', 'date', 'hours', 'minutes' or 'seconds'. For spinner type date only.
 *
 * @method uiSpinner
 * @member jQuery
 *
 * @since 6.11.0
 */

/*
 * @copyright        © Copyright 2006-2010, epages GmbH, All Rights Reserved.
 *
 * @module            ep.ui.spinner
 *
 * @revision        $Revision: 1.19 $
 */

/*jslint browser: true, nomen: true, plusplus: true*/
/*global define*/

define("ep/ui/spinner", [
    "jquery",
    "ep",

    "ep/i18n",
    "ep/date",
    "ep/sprite",
    "ep/ui/validate"
], function ($, ep) {

    'use strict';

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

    var is = function (val) {
            return !!(val === 0 || (!isNaN(val) && val));
        },
        html = $('html');

    $.widget('ui.uiSpinner', $.ui.uiValidate, {

        options: {
            // ep.ui.Input
            //    big:                true,
            //    autofocus:            false,
            //    info:                'text',

            // ep.ui.Validate
            //    accept:                '',
            //    min:                1,
            //    max:                20,
            //    minlength:            1,
            //    maxlength:            4,
            //    pattern:            [\w]+,
            //    required:            true,
            //    tooltipOffset:        [0,-7],
            //    valid:                true,
            //    type:                'basic',
            //    format:                '',

            // ep.ui.Spinner
            //    unit:                'GByte',
            type: 'number',
            format: 'n0',
            step: 1,
            stepType: 'hours',
            validateStep: false
        },

        _create: function () {
            var self = this;

            this._superApply(arguments);

            this.options = $.extend({
                step: parseInt(this.elem.attr('step'), 10)
            }, this.options);

            this.elem.addClass('ep-uiSpinner')
                .on('keydown', function (event) {
                    if (event.keyCode === 38 || event.keyCode === 40) {
                        event.preventDefault();
                        if (!self._interval) {
                            self._start(event.keyCode === 38 ? 1 : -1);
                        }
                    }
                })
                .on('keyup', function (event) {
                    if (event.keyCode === 38 || event.keyCode === 40) {
                        event.preventDefault();
                        self._stop();
                    }
                });

            this.stepper = $('<span>')
                .addClass('ep-uiInput ep-uiInput-button ep-uiSpinner-stepper')
                .insertAfter(this.elem);

            this.stepUp = ep(this.dict.parse('<a href="javascript:;">+</a>'))
                .addClass('ep-uiSpinner-stepUp')
                .attr({
                    'tabIndex': -1
                })
                .on('mousedown.uiSpinner', function (event) {
                    if (event.button === 0) {
                        self._start(1);
                    }
                })
                .appendTo(this.stepper);

            this.stepDown = ep(this.dict.parse('<a href="javascript:;">&ndash;</a>'))
                .addClass('ep-uiSpinner-stepDown')
                .attr({
                    'tabIndex': -1
                })
                .on('mousedown.uiSpinner', function (event) {
                    if (event.button === 0) {
                        self._start(-1);
                    }
                })
                .appendTo(this.stepper);

            this.stack.push(this.stepper[0], this.stepUp[0], this.stepDown[0]);
        },

        _init: function () {
            var o = this.options,
                formatMatch,
                formatDecimals,
                stepSplit,
                stepDecimals,
                minSplit,
                minDecimals,
                maxSplit,
                maxDecimals,
                decimals;

            this._superApply(arguments);
            this._unitInit();

            // Detect format by format,step, min and max options
            if (o.type !== 'date') {
                formatMatch = (/(c|n|p)(\d{0,})/).exec(o.format);
                formatDecimals = parseInt(formatMatch[2] || 0, 10);

                stepSplit = o.step.toString().split('.');
                stepDecimals = stepSplit[1] ? stepSplit[1].length : 0;

                minSplit = o.min.toString().split('.');
                minDecimals = minSplit[1] ? minSplit[1].length : 0;

                maxSplit = o.max.toString().split('.');
                maxDecimals = maxSplit[1] ? maxSplit[1].length : 0;

                // Get highest value of decimals from format/step/min/max
                decimals = Math.max(Math.max(Math.max(formatDecimals, stepDecimals), minDecimals), maxDecimals);

                this.options.format = formatMatch[1] + decimals;
            }
        },

        _unitInit: function () {
            var padding;

            if (!this.unit && this.options.unit) {
                this.unit = $('<span>')
                    .addClass('ep-uiSpinner-unit')
                    .insertBefore(this.stepper);

                this.stack.push(this.unit[0]);
            }

            if (this.unit) {
                this.unit.text(this.options.unit);

                padding = (this.unit.width() + 4) + 'px';

                this.elem.css('padding-right', padding);
                this.unit.css('margin-left', '-' + padding);
            }
        },

        _tooltipInit: function () {
            this._superApply(arguments);

            if (this.tooltip) {
                this.tooltip.uiTooltip('option', 'offsetAdjust', [30, -1]);
            }
        },

        _changeAttr: function (event, attr) {
            var self = this;

            $.each(attr, function (name, value) {
                if ((/^(accept|pattern|required)$/i).test(name)) {
                    self.options[name] = value;
                } else if ((/^(min|max|minlength|maxlength|step)$/i).test(name)) {
                    self.options[name] = parseInt(value, 10);
                }
            });
        },

        _parseValue: function () {
            var o = this.options,
                region = {
                    region: o.region
                },
                val = this.elem.attr('value');

            if (o.type === 'date') {
                if (!is(this._val)) {
                    this._val = is(o.min) ? new ep.Date(o.min) : new ep.Date();
                }

                val = $.i18n.parseDate(val, o.format, region);

                this._val = is(val) ? new ep.Date(val) : this._val;
            } else {
                if (!is(this._val)) {
                    this._val = is(o.min) ? o.min : 0;
                }

                region.currency = o.currency;

                val = $.i18n.parseNumber(val, 10, region);

                this._val = is(val) ? val : this._val;
            }
        },

        _start: function (inc) {
            this._parseValue();

            this._type = '_spin' + (this.options.type === 'date' ? 'Date' : 'Number');
            this._step = this.options.step * inc;
            this._count = 0;
            this[this._type]();

            // set interval for auto count up/down
            this._interval = setInterval($.proxy(this, '_spin'), 250);

            html.on('mouseup.uiSpinner', $.proxy(this, '_stop'));
        },

        _spin: function () {
            this._count++;

            if ((/^(10|20|30)$/).test(this._count.toString())) {
                clearInterval(this._interval);
                // update interval speed for auto count up/down
                this._interval = setInterval($.proxy(this, '_spin'), 150 / this._count);
            }

            this[this._type]();
        },

        _spinNumber: function () {
            var o = this.options;

            this._val += this._step;

            if (is(o.min) && o.min > this._val) {
                this._val = o.min;
                this._stop();
            } else if (is(o.max) && o.max < this._val) {
                this._val = o.max;
                this._stop();
            }

            this.elem.val($.i18n.formatNumber(this._val, o.format, {
                region: o.region,
                currency: o.currency
            }));
        },

        _spinDate: function () {
            var o = this.options;

            this._val[$.camelCase('add-' + o.stepType)](this._step);

            if (is(o.min) && o.min > this._val.getTime()) {
                this._val = new ep.Date(o.min);
                this._stop();
            } else if (is(o.max) && o.max < this._val.getTime()) {
                this._val = new ep.Date(o.max);
                this._stop();
            }

            this.elem.val($.i18n.formatDate(this._val, o.format, {
                region: o.region
            }));
        },

        _stop: function () {
            html.off('mouseup.uiSpinner', $.proxy(this, '_stop'));

            this._interval = clearInterval(this._interval);


            this.elem.filter(':visible')
                .focus()
                .end()
                .trigger('changeValue');
        },

        _createErrorMsgOptions: function () {
            var self = this,
                o = self.options,
                tmpOptions = this._superApply(arguments),
                exampleValue;

            if (this.valid === "INVALID_STEP") {
                exampleValue = o.min + ', ' + (o.min + o.step) + ', ' + (o.min + 2 * o.step);

                exampleValue = {
                    example: exampleValue
                };
            }

            return exampleValue || tmpOptions;
        },

        _validate: function (event) {
            var self = this,
                o = self.options,
                val = $.i18n.parseNumber(this.elem.val());

            this._superApply(arguments);

            if (o.validateStep && this.valid === true) {
                if (((val - o.min)
                    .toFixed(3) * 1000).toFixed(3) % (o.step * 1000)) {
                    this._setValidStatus("INVALID_STEP");
                    if (event.type !== 'changeAttr') {
                        $(this.elem[0].form)
                            .trigger('change');
                    }
                }
            }
        },

        destroy: function () {
            this.elem.off('.uiSpinner')
                .removeClass('epWidth-29')
                .removeClass(/ep-uiSpinner[\w\d\-]*/g);

            this._superApply(arguments);
        }
    });

    return ep;

});