(function() {
  define(["./dom", "underscore", "./events"], function(dom, _, events) {
    var PaletteController, isSelected;
    isSelected = function(option) {
      return option.selected;
    };
    PaletteController = (function() {
      function PaletteController(id) {
        var i, j, len, option, ref;
        this.selected = dom(id);
        this.container = this.selected.findParent(".palette");
        this.available = this.container.findFirst(".palette-available select");
        this.hidden = this.container.findFirst("input[type=hidden]");
        this.select = this.container.findFirst("[data-action=select]");
        this.deselect = this.container.findFirst("[data-action=deselect]");
        this.moveUp = this.container.findFirst("[data-action=move-up]");
        this.moveDown = this.container.findFirst("[data-action=move-down]");
        this.reorder = this.moveUp !== null;
        this.valueToOrderIndex = {};
        ref = this.available.element.options;
        for (i = j = 0, len = ref.length; j < len; i = ++j) {
          option = ref[i];
          this.valueToOrderIndex[option.value] = i;
        }
        this.initialTransfer();
        if (!this.selected.element.disabled) {
          this.updateButtons();
          this.bindEvents();
        }
      }

      PaletteController.prototype.initialTransfer = function() {
        var e, i, j, k, l, len, len1, movers, option, pos, ref, results, v, value, valueToPosition, values;
        values = JSON.parse(this.hidden.value());
        valueToPosition = {};
        for (i = j = 0, len = values.length; j < len; i = ++j) {
          v = values[i];
          valueToPosition[v] = i;
        }
        e = this.available.element;
        movers = [];
        for (i = k = ref = e.options.length - 1; k >= 0; i = k += -1) {
          option = e.options[i];
          value = option.value;
          pos = valueToPosition[value];
          if (pos !== void 0) {
            movers[pos] = option;
            e.remove(i);
          }
        }
        results = [];
        for (l = 0, len1 = movers.length; l < len1; l++) {
          option = movers[l];
          results.push(this.selected.element.add(option));
        }
        return results;
      };

      PaletteController.prototype.updateAfterChange = function() {
        this.updateHidden();
        return this.updateButtons();
      };

      PaletteController.prototype.updateHidden = function() {
        var option, values;
        values = (function() {
          var j, len, ref, results;
          ref = this.selected.element.options;
          results = [];
          for (j = 0, len = ref.length; j < len; j++) {
            option = ref[j];
            results.push(option.value);
          }
          return results;
        }).call(this);
        return this.hidden.value(JSON.stringify(values));
      };

      PaletteController.prototype.bindEvents = function() {
        this.container.on("change", "select", (function(_this) {
          return function() {
            _this.updateButtons();
            return false;
          };
        })(this));
        this.select.on("click", (function(_this) {
          return function() {
            _this.doSelect();
            return false;
          };
        })(this));
        this.available.on("dblclick", (function(_this) {
          return function() {
            _this.doSelect();
            return false;
          };
        })(this));
        this.deselect.on("click", (function(_this) {
          return function() {
            _this.doDeselect();
            return false;
          };
        })(this));
        this.selected.on("dblclick", (function(_this) {
          return function() {
            _this.doDeselect();
            return false;
          };
        })(this));
        if (this.reorder) {
          this.moveUp.on("click", (function(_this) {
            return function() {
              _this.doMoveUp();
              return false;
            };
          })(this));
          return this.moveDown.on("click", (function(_this) {
            return function() {
              _this.doMoveDown();
              return false;
            };
          })(this));
        }
      };

      PaletteController.prototype.updateButtons = function() {
        var nothingSelected;
        this.select.element.disabled = this.available.element.selectedIndex < 0;
        nothingSelected = this.selected.element.selectedIndex < 0;
        this.deselect.element.disabled = nothingSelected;
        if (this.reorder) {
          this.moveUp.element.disabled = nothingSelected || this.allSelectionsAtTop();
          return this.moveDown.element.disabled = nothingSelected || this.allSelectionsAtBottom();
        }
      };

      PaletteController.prototype.doSelect = function() {
        return this.transferOptions(this.available, this.selected, this.reorder);
      };

      PaletteController.prototype.doDeselect = function() {
        return this.transferOptions(this.selected, this.available, false);
      };

      PaletteController.prototype.doMoveUp = function() {
        var firstMoverIndex, groups, j, len, movers, o, options, pivot, splicePos;
        options = _.toArray(this.selected.element.options);
        groups = _.partition(options, isSelected);
        movers = groups[0];
        firstMoverIndex = _.first(movers).index;
        pivot = options[firstMoverIndex - 1];
        options = groups[1];
        splicePos = pivot ? _.indexOf(options, pivot) : 0;
        movers.reverse();
        for (j = 0, len = movers.length; j < len; j++) {
          o = movers[j];
          options.splice(splicePos, 0, o);
        }
        return this.reorderSelected(options);
      };

      PaletteController.prototype.doMoveDown = function() {
        var groups, j, lastMoverIndex, len, movers, o, options, pivot, splicePos;
        options = _.toArray(this.selected.element.options);
        groups = _.partition(options, isSelected);
        movers = groups[0];
        lastMoverIndex = movers.slice(-1)[0].index;
        pivot = options[lastMoverIndex + 1];
        options = groups[1];
        splicePos = pivot ? _.indexOf(options, pivot) + 1 : options.length;
        movers.reverse();
        for (j = 0, len = movers.length; j < len; j++) {
          o = movers[j];
          options.splice(splicePos, 0, o);
        }
        return this.reorderSelected(options);
      };

      PaletteController.prototype.reorderSelected = function(options) {
        return this.performUpdate(true, options, (function(_this) {
          return function() {
            var j, len, o, results;
            _this.deleteOptions(_this.selected);
            results = [];
            for (j = 0, len = options.length; j < len; j++) {
              o = options[j];
              results.push(_this.selected.element.add(o, null));
            }
            return results;
          };
        })(this));
      };

      PaletteController.prototype.performUpdate = function(reorder, selectedOptions, updateCallback) {
        var canceled, doUpdate, memo;
        canceled = false;
        doUpdate = (function(_this) {
          return function() {
            updateCallback();
            _this.selected.trigger(events.palette.didChange, {
              selectedOptions: selectedOptions,
              reorder: reorder
            });
            return _this.updateAfterChange();
          };
        })(this);
        memo = {
          selectedOptions: selectedOptions,
          reorder: reorder,
          cancel: function() {
            return canceled = true;
          },
          defer: function() {
            canceled = true;
            return doUpdate;
          }
        };
        this.selected.trigger(events.palette.willChange, memo);
        if (!canceled) {
          return doUpdate();
        }
      };

      PaletteController.prototype.deleteOptions = function(select) {
        var e, i, j, ref, results;
        e = select.element;
        results = [];
        for (i = j = ref = e.length - 1; j >= 0; i = j += -1) {
          results.push(e.remove(i));
        }
        return results;
      };

      PaletteController.prototype.transferOptions = function(from, to, atEnd) {
        var fromOptions, j, len, movers, o, selectedOptions, toOptions;
        if (from.element.selectedIndex === -1) {
          return;
        }
        movers = _.filter(from.element.options, isSelected);
        fromOptions = _.reject(from.element.options, isSelected);
        toOptions = _.toArray(to.element.options);
        for (j = 0, len = movers.length; j < len; j++) {
          o = movers[j];
          this.insertOption(toOptions, o, atEnd);
        }
        selectedOptions = to === this.selected ? toOptions : fromOptions;
        return this.performUpdate(false, selectedOptions, (function(_this) {
          return function() {
            var i, k, l, len1, m, ref, ref1, results;
            for (i = k = ref = from.element.length - 1; k >= 0; i = k += -1) {
              if (from.element.options[i].selected) {
                from.element.remove(i);
              }
            }
            for (i = l = ref1 = to.element.length - 1; l >= 0; i = l += -1) {
              to.element.options[i].selected = false;
              to.element.remove(i);
            }
            results = [];
            for (m = 0, len1 = toOptions.length; m < len1; m++) {
              o = toOptions[m];
              results.push(to.element.add(o, null));
            }
            return results;
          };
        })(this));
      };

      PaletteController.prototype.insertOption = function(options, option, atEnd) {
        var before, i, optionOrder;
        if (!atEnd) {
          optionOrder = this.valueToOrderIndex[option.value];
          before = _.find(options, (function(_this) {
            return function(o) {
              return _this.valueToOrderIndex[o.value] > optionOrder;
            };
          })(this));
        }
        if (before) {
          i = _.indexOf(options, before);
          return options.splice(i, 0, option);
        } else {
          return options.push(option);
        }
      };

      PaletteController.prototype.indexOfLastSelection = function(select) {
        var e, i, j, ref, ref1;
        e = select.element;
        if (e.selectedIndex < 0) {
          return -1;
        }
        for (i = j = ref = e.options.length - 1, ref1 = e.selectedIndex; j >= ref1; i = j += -1) {
          if (e.options[i].selected) {
            return i;
          }
        }
        return -1;
      };

      PaletteController.prototype.allSelectionsAtTop = function() {
        var last, options;
        last = this.indexOfLastSelection(this.selected);
        options = _.toArray(this.selected.element.options);
        return _(options.slice(0, +last + 1 || 9e9)).all(function(o) {
          return o.selected;
        });
      };

      PaletteController.prototype.allSelectionsAtBottom = function() {
        var e, last, options;
        e = this.selected.element;
        last = e.selectedIndex;
        options = _.toArray(e.options);
        return _(options.slice(last)).all(function(o) {
          return o.selected;
        });
      };

      return PaletteController;

    })();
    return function(id) {
      return new PaletteController(id);
    };
  });

}).call(this);
