/**
Typeahead input (bootstrap only). Based on Twitter Bootstrap [typeahead](http://twitter.github.com/bootstrap/javascript.html#typeahead).  
Depending on `source` format typeahead operates in two modes:

* **strings**:  
  When `source` defined as array of strings, e.g. `['text1', 'text2', 'text3' ...]`.  
  User can submit one of these strings or any text entered in input (even if it is not matching source).
  
* **objects**:  
  When `source` defined as array of objects, e.g. `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`.  
  User can submit only values that are in source (otherwise `null` is submitted). This is more like *dropdown* behavior.

@class typeahead
@extends list
@since 1.4.1
@final
@example
<a href="#" id="country" data-type="typeahead" data-pk="1" data-url="/post" data-original-title="Input country"></a>
<script>
$(function(){
    $('#country').editable({
        value: 'ru',    
        source: [
              {value: 'gb', text: 'Great Britain'},
              {value: 'us', text: 'United States'},
              {value: 'ru', text: 'Russia'}
           ]
    });
});
</script>
**/
(function ($) {

    var Constructor = function (options) {
        this.init('typeahead', options, Constructor.defaults);
        
        //overriding objects in config (as by default jQuery extend() is not recursive)
        this.options.typeahead = $.extend({}, Constructor.defaults.typeahead, {
            //set default methods for typeahead to work with objects
            matcher: this.matcher,  
            sorter: this.sorter,  
            highlighter: this.highlighter,  
            updater: this.updater  
        }, options.typeahead);
    };

    $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.list);

    $.extend(Constructor.prototype, {
        renderList: function() {
            this.$input = this.$tpl.is('input') ? this.$tpl : this.$tpl.find('input[type="text"]');
            
            //set source of typeahead
            this.options.typeahead.source = this.sourceData;
            
            //apply typeahead
            this.$input.typeahead(this.options.typeahead);
            
            //patch some methods in typeahead
            var ta = this.$input.data('typeahead');
            ta.render = $.proxy(this.typeaheadRender, ta);
            ta.select = $.proxy(this.typeaheadSelect, ta);
            ta.move = $.proxy(this.typeaheadMove, ta);

            this.renderClear();
            this.setClass();
            this.setAttr('placeholder');
        },
       
        value2htmlFinal: function(value, element) {
            if(this.getIsObjects()) {
                var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
                $(element).text(items.length ? items[0].text : '');
            } else {
                $(element).text(value);
            }
        },
        
        html2value: function (html) {
            return html ? html : null;
        },
        
        value2input: function(value) {
            if(this.getIsObjects()) {
                var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
                this.$input.data('value', value).val(items.length ? items[0].text : '');                
            } else {
                this.$input.val(value);
            }
        },
        
        input2value: function() {
            if(this.getIsObjects()) {
                var value = this.$input.data('value'),
                    items = $.fn.editableutils.itemsByValue(value, this.sourceData);
                    
                if(items.length && items[0].text.toLowerCase() === this.$input.val().toLowerCase()) {
                   return value;
                } else {
                   return null; //entered string not found in source
                }                 
            } else {
                return this.$input.val();
            }
        },
        
        /*
         if in sourceData values <> texts, typeahead in "objects" mode: 
         user must pick some value from list, otherwise `null` returned.
         if all values == texts put typeahead in "strings" mode:
         anything what entered is submited.
        */        
        getIsObjects: function() {
            if(this.isObjects === undefined) {
                this.isObjects = false;
                for(var i=0; i<this.sourceData.length; i++) {
                    if(this.sourceData[i].value !== this.sourceData[i].text) {
                        this.isObjects = true;
                        break;
                    } 
                }
            } 
            return this.isObjects;
        },  
               
        /*
          Methods borrowed from text input
        */
        activate: $.fn.editabletypes.text.prototype.activate,
        renderClear: $.fn.editabletypes.text.prototype.renderClear,
        postrender: $.fn.editabletypes.text.prototype.postrender,
        toggleClear: $.fn.editabletypes.text.prototype.toggleClear,
        clear: function() {
            $.fn.editabletypes.text.prototype.clear.call(this);
            this.$input.data('value', ''); 
        },
        
        
        /*
          Typeahead option methods used as defaults
        */
        /*jshint eqeqeq:false, curly: false, laxcomma: true, asi: true*/
        matcher: function (item) {
            return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text);
        },
        sorter: function (items) {
            var beginswith = []
            , caseSensitive = []
            , caseInsensitive = []
            , item
            , text;

            while (item = items.shift()) {
                text = item.text;
                if (!text.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item);
                else if (~text.indexOf(this.query)) caseSensitive.push(item);
                else caseInsensitive.push(item);
            }

            return beginswith.concat(caseSensitive, caseInsensitive);
        },
        highlighter: function (item) {
            return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text);
        },
        updater: function (item) {
            this.$element.data('value', item.value);
            return item.text;
        },  
   
        
        /*
          Overwrite typeahead's render method to store objects.
          There are a lot of disscussion in bootstrap repo on this point and still no result.
          See https://github.com/twitter/bootstrap/issues/5967 
          
          This function just store item via jQuery data() method instead of attr('data-value')
        */        
        typeaheadRender: function (items) {
            var that = this;

            items = $(items).map(function (i, item) {
//                i = $(that.options.item).attr('data-value', item)
                i = $(that.options.item).data('item', item);
                i.find('a').html(that.highlighter(item));
                return i[0];
            });

            //add option to disable autoselect of first line
            //see https://github.com/twitter/bootstrap/pull/4164 
            if (this.options.autoSelect) {
              items.first().addClass('active');
            }
            this.$menu.html(items);
            return this;
        },
       
        //add option to disable autoselect of first line
        //see https://github.com/twitter/bootstrap/pull/4164         
        typeaheadSelect: function () {
          var val = this.$menu.find('.active').data('item')
          if(this.options.autoSelect || val){
            this.$element
            .val(this.updater(val))
            .change()
          }
          return this.hide()
        },
        
        /*
         if autoSelect = false and nothing matched we need extra press onEnter that is not convinient.
         This patch fixes it.
        */
        typeaheadMove: function (e) {
          if (!this.shown) return

          switch(e.keyCode) {
            case 9: // tab
            case 13: // enter
            case 27: // escape
              if (!this.$menu.find('.active').length) return
              e.preventDefault()
              break

            case 38: // up arrow
              e.preventDefault()
              this.prev()
              break

            case 40: // down arrow
              e.preventDefault()
              this.next()
              break
          }

          e.stopPropagation()
        }
        
        /*jshint eqeqeq: true, curly: true, laxcomma: false, asi: false*/  
        
    });      

    Constructor.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
        /**
        @property tpl 
        @default <input type="text">
        **/         
        tpl:'<input type="text">',
        /**
        Configuration of typeahead. [Full list of options](http://twitter.github.com/bootstrap/javascript.html#typeahead).
        
        @property typeahead 
        @type object
        @default null
        **/
        typeahead: null,
        /**
        Whether to show `clear` button 
        
        @property clear 
        @type boolean
        @default true        
        **/
        clear: true
    });

    $.fn.editabletypes.typeahead = Constructor;      
    
}(window.jQuery));