define([

  './base',

  '../utils',

  'jquery'

], function (BaseAdapter, Utils, $) {

  function SelectAdapter ($element, options) {

    this.$element = $element;

    this.options = options;



    SelectAdapter.__super__.constructor.call(this);

  }



  Utils.Extend(SelectAdapter, BaseAdapter);



  SelectAdapter.prototype.current = function (callback) {

    var data = [];

    var self = this;



    this.$element.find(':selected').each(function () {

      var $option = $(this);



      var option = self.item($option);



      data.push(option);

    });



    callback(data);

  };



  SelectAdapter.prototype.select = function (data) {

    var self = this;



    data.selected = true;



    // If data.element is a DOM node, use it instead

    if ($(data.element).is('option')) {

      data.element.selected = true;



      this.$element.trigger('change');



      return;

    }



    if (this.$element.prop('multiple')) {

      this.current(function (currentData) {

        var val = [];



        data = [data];

        data.push.apply(data, currentData);



        for (var d = 0; d < data.length; d++) {

          var id = data[d].id;



          if ($.inArray(id, val) === -1) {

            val.push(id);

          }

        }



        self.$element.val(val);

        self.$element.trigger('change');

      });

    } else {

      var val = data.id;



      this.$element.val(val);

      this.$element.trigger('change');

    }

  };



  SelectAdapter.prototype.unselect = function (data) {

    var self = this;



    if (!this.$element.prop('multiple')) {

      return;

    }



    data.selected = false;



    if ($(data.element).is('option')) {

      data.element.selected = false;



      this.$element.trigger('change');



      return;

    }



    this.current(function (currentData) {

      var val = [];



      for (var d = 0; d < currentData.length; d++) {

        var id = currentData[d].id;



        if (id !== data.id && $.inArray(id, val) === -1) {

          val.push(id);

        }

      }



      self.$element.val(val);



      self.$element.trigger('change');

    });

  };



  SelectAdapter.prototype.bind = function (container, $container) {

    var self = this;



    this.container = container;



    container.on('select', function (params) {

      self.select(params.data);

    });



    container.on('unselect', function (params) {

      self.unselect(params.data);

    });

  };



  SelectAdapter.prototype.destroy = function () {

    // Remove anything added to child elements

    this.$element.find('*').each(function () {

      // Remove any custom data set by Select2

      $.removeData(this, 'data');

    });

  };



  SelectAdapter.prototype.query = function (params, callback) {

    var data = [];

    var self = this;



    var $options = this.$element.children();



    $options.each(function () {

      var $option = $(this);



      if (!$option.is('option') && !$option.is('optgroup')) {

        return;

      }



      var option = self.item($option);



      var matches = self.matches(params, option);



      if (matches !== null) {

        data.push(matches);

      }

    });



    callback({

      results: data

    });

  };



  SelectAdapter.prototype.addOptions = function ($options) {

    Utils.appendMany(this.$element, $options);

  };



  SelectAdapter.prototype.option = function (data) {

    var option;



    if (data.children) {

      option = document.createElement('optgroup');

      option.label = data.text;

    } else {

      option = document.createElement('option');



      if (option.textContent !== undefined) {

        option.textContent = data.text;

      } else {

        option.innerText = data.text;

      }

    }



    if (data.id !== undefined) {

      option.value = data.id;

    }



    if (data.disabled) {

      option.disabled = true;

    }



    if (data.selected) {

      option.selected = true;

    }



    if (data.title) {

      option.title = data.title;

    }



    var $option = $(option);



    var normalizedData = this._normalizeItem(data);

    normalizedData.element = option;



    // Override the option's data with the combined data

    $.data(option, 'data', normalizedData);



    return $option;

  };



  SelectAdapter.prototype.item = function ($option) {

    var data = {};



    data = $.data($option[0], 'data');



    if (data != null) {

      return data;

    }



    if ($option.is('option')) {

      data = {

        id: $option.val(),

        text: $option.text(),

        disabled: $option.prop('disabled'),

        selected: $option.prop('selected'),

        title: $option.prop('title')

      };

    } else if ($option.is('optgroup')) {

      data = {

        text: $option.prop('label'),

        children: [],

        title: $option.prop('title')

      };



      var $children = $option.children('option');

      var children = [];



      for (var c = 0; c < $children.length; c++) {

        var $child = $($children[c]);



        var child = this.item($child);



        children.push(child);

      }



      data.children = children;

    }



    data = this._normalizeItem(data);

    data.element = $option[0];



    $.data($option[0], 'data', data);



    return data;

  };



  SelectAdapter.prototype._normalizeItem = function (item) {

    if (!$.isPlainObject(item)) {

      item = {

        id: item,

        text: item

      };

    }



    item = $.extend({}, {

      text: ''

    }, item);



    var defaults = {

      selected: false,

      disabled: false

    };



    if (item.id != null) {

      item.id = item.id.toString();

    }



    if (item.text != null) {

      item.text = item.text.toString();

    }



    if (item._resultId == null && item.id && this.container != null) {

      item._resultId = this.generateResultId(this.container, item);

    }



    return $.extend({}, defaults, item);

  };



  SelectAdapter.prototype.matches = function (params, data) {

    var matcher = this.options.get('matcher');



    return matcher(params, data);

  };



  return SelectAdapter;

});

