/**
 * @revision        $Revision: 1.5 $
 */

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

/**
 * @class jQuery.ui.uiSmartsearch
 * @author Copyright 2006-2013, epages GmbH, All Rights Reserved
 *
 * The ui smartsearch widget can be used to extend an input field's
 * functionality with suggestions retrieved from an internal or external
 * data source.
 *
 * ### Example
 * HTML:
 *
 *     <input class="FullSize ep-ui-smartsearch ep-js" data-js=".uiInput()" value="" name="TagNamesString" />
 *
 * JavaScript:
 *
 *     $('.ep-ui-smartsearch').uiSmartsearch({
 *         'source': ['Suggestion1', 'Suggestion2', 'Suggestion3', 'Suggestion4'],
 *         'multiple' : true
 *     });
 *
 * @uses jQuery
 * @uses ep
 * @uses jQuery.tmpl
 * @uses jQuery.ui.widget
 * @uses jQuery.ui.autocomplete
 * @uses ep.ajax
 *
 * @chainable
 * @return {Object} ep
 */
define("ep/ui/smartsearch", [
    "jquery",
    "ep",

    "jquery/tmpl",
    "jquery/ui/widget",
    "jquery/ui/autocomplete",
    "ep/ajax"
], function ($, ep) {
    'use strict';

    var groupedSource = {};

    $.widget('ui.uiSmartsearch', {

        /**
         * Options to be passed to the widgt on initialzation
         *
         * @cfg {Object}         options
         * @cfg {Array | String} options.source Source to look for suggestions (when string we use
         *                           it as view action)
         * @cfg {Object}        [options.additionalParameters={}] An hash map of additional
         *                           parameters appended to the request. The keys are taken as
         *                           parameter names while the values are used as parameter values.
         * @cfg {String}        [options.group=undefined] Identifier for a group of widgets that use
         *                           the same data source
         * @cfg {Boolean}       [options.multiple=false] Allow one or multiple entries
         * @cfg {Number}        [options.minLength=1] The minimum number of characters a user must
         *                           type before a search is performed
         */
        options: {
            'source': [],
            'group': undefined,
            'multiple': false,
            'minLength': 1
        },

        /**
         * Creates widget instance and sets grouped data source
         *
         * @since 6.17.0
         * @private
         */
        _create: function () {
            var self = this,
                o = self.options;

            if (o.group && groupedSource && !groupedSource[o.group]) {
                groupedSource[o.group] = o.source;
            }
        },

        /**
         * Initializes widget and adds configured autocomple widget to the element on that it's
         * called.
         *
         * @since 6.17.0
         * @private
         */
        _init: function () {
            var self = this,
                o = self.options;

            self.element.autocomplete({
                minLength: o.minLength,

                source: function (request, response) {
                    var term = '',
                        returnValue = [];

                    if (o.multiple) {
                        term = request.term.split(/,\s*/).pop();
                    } else {
                        term = request.term;
                    }

                    if (o.source instanceof Array) {
                        returnValue = $.ui.autocomplete.filter(o.source, term);
                        response(returnValue);
                    } else if (typeof o.source === 'string') {
                        ep.ajax({
                            dataType: 'json',
                            data: $.extend({
                                'ObjectID': ep.config.siteId,
                                'ViewAction': o.source,
                                'SearchString': request.term
                            }, o.searchOptions)
                        }).done(function (data) {
                            response($.map(data.Results, function (item) {
                                return {
                                    label: item.alias,
                                    value: item.alias,
                                    desc: item.name
                                };
                            }));
                        });
                    }
                },

                focus: function () {
                    return false;
                },

                select: function (event, ui) {
                    if (o.multiple) {
                        var terms = this.value.split(/,\s*/);
                        terms.pop();
                        terms.push(ui.item.value);
                        terms.push("");
                        this.value = terms.join(", ");
                    } else {
                        this.value = ui.item.value;
                    }
                    return false;
                }
            }).data("ui-autocomplete")._renderItem = function (ul, item) {
                var returnNode = $("<li>");
                if (item.desc) {
                    returnNode.append("<a>" + item.value + " - " + ep.scrunch(item.desc, 25) + "</a>");
                } else {
                    returnNode.append("<a>" + item.value + "</a>");
                }
                returnNode.appendTo(ul);
                return returnNode;
            };

            self.element.on("keydown", function (event) {
                if (event.keyCode === $.ui.keyCode.TAB && $(this).data("ui-autocomplete").menu.active) {
                    event.preventDefault();
                }
            });

            //if a group is defined we need to provide all aditional added tags to all group members
            if (o.group) {
                self.element.on("change", function (event) {
                    self._checkTags($(this).val().split(','), groupedSource[o.group]);
                });
            }
        },

        /**
         * Checks if tag is already present in data source
         *
         * @since 6.17.0
         * @private
         *
         * @param  {Array} newTags Tags to add to the data source
         * @param  {Array} source  Data source
         */
        _checkTags: function (newTags, source) {
            $.each(newTags, function (i, elem) {
                if ($.inArray(elem, source) === -1) {
                    source.push(elem);
                }
            });
        },

        /**
         * Destroys the autocomplete widget
         *
         * @since 6.17.0
         */
        destroy: function () {
            this.element.autocomplete('destroy');
            this._superApply(arguments);
        }
    });
    return ep;
});