var portrayalSwatchBinding = new Shiny.InputBinding();

$.extend(portrayalSwatchBinding, {
  find: function(scope) {
    return $(scope).find(".portrayal-swatch-input");
  },
  getValue: function(el) {
    return $(el).data("value") || null;
  },
  setValue: function(el, value) {
    $(el).data("value", value);
    $(el).find(".portrayal-swatch").each(function() {
      var isSelected = $(this).data("value") === value;
      $(this)
        .toggleClass("selected", isSelected)
        .attr("aria-selected", isSelected ? "true" : "false");
    });
  },
  subscribe: function(el, callback) {
    var $el = $(el);

    // click
    $el.on("click.portrayalSwatch", ".portrayal-swatch", function() {
      var value = $(this).data("value");
      portrayalSwatchBinding.setValue(el, value);
      callback();
    });

    // keyboard
    $el.on("keydown.portrayalSwatch", function(e) {
      var $items = $el.find(".portrayal-swatch");
      var current = portrayalSwatchBinding.getValue(el);
      var idx = $items.index($items.filter(function() {
        return $(this).data("value") === current;
      }));

      if (idx < 0) idx = 0;

      var nextIdx = idx;

      if (e.key === "ArrowRight" || e.key === "ArrowDown") {
        nextIdx = (idx + 1) % $items.length;
      } else if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
        nextIdx = (idx - 1 + $items.length) % $items.length;
      } else if (e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        portrayalSwatchBinding.setValue(el, current);
        callback();
        return;
      } else {
        return;
      }

      e.preventDefault();
      var nextValue = $items.eq(nextIdx).data("value");
      portrayalSwatchBinding.setValue(el, nextValue);
      callback();
    });
  },
  unsubscribe: function(el) {
    $(el).off(".portrayalSwatch");
  },
  receiveMessage: function(el, data) {
    if (data.hasOwnProperty("value")) {
      portrayalSwatchBinding.setValue(el, data.value);
    }
  }
});

Shiny.inputBindings.register(portrayalSwatchBinding, "portrayal.swatchInput");
