/**
 * @provides javelin-behavior-comment-actions
 * @requires javelin-behavior
 *           javelin-stratcom
 *           javelin-workflow
 *           javelin-dom
 *           phuix-form-control-view
 *           phuix-icon-view
 *           javelin-behavior-phabricator-gesture
 */

JX.behavior('comment-actions', function(config) {
  var action_map = config.actions;

  var action_node = JX.$(config.actionID);
  var form_node = JX.$(config.formID);
  var input_node = JX.$(config.inputID);
  var place_node = JX.$(config.placeID);

  var rows = {};

  JX.DOM.listen(action_node, 'change', null, function() {
    var option = find_option(action_node.value);

    action_node.value = '+';

    if (option) {
      add_row(option);
    }
  });

  function find_option(key) {
    var options = action_node.options;
    var option;

    for (var ii = 0; ii < options.length; ii++) {
      option = options[ii];
      if (option.value == key) {
        return option;
      }
    }

    return null;
  }

  function redraw() {
    // If any of the stacked actions specify that they change the label for
    // the "Submit" button, update the button text. Otherwise, return it to
    // the default text.
    var button_text = config.defaultButtonText;
    for (var k in rows) {
      var action = action_map[k];
      if (action.buttonText) {
        button_text = action.buttonText;
      }
    }

    var button_node = JX.DOM.find(form_node, 'button', 'submit-transactions');
    JX.DOM.setContent(button_node, button_text);
  }

  function remove_action(key) {
    var row = rows[key];
    if (row) {
      JX.DOM.remove(row.node);
      row.option.disabled = false;
      delete rows[key];
    }

    redraw();
  }

  function serialize_actions() {
    var data = [];

    for (var k in rows) {
      data.push({
        type: k,
        value: rows[k].control.getValue(),
        initialValue: action_map[k].initialValue || null
      });
    }

    return JX.JSON.stringify(data);
  }

  function get_data() {
    var data = JX.DOM.convertFormToDictionary(form_node);

    data.__preview__ = 1;
    data[input_node.name] = serialize_actions();

    data.viewData = JX.JSON.stringify(config.viewData);

    return data;
  }

  function restore_draft_actions(drafts) {
    var draft;
    var option;
    var control;

    for (var ii = 0; ii < drafts.length; ii++) {
      draft = drafts[ii];

      option = find_option(draft);
      if (!option) {
        continue;
      }

      control = add_row(option);
    }

    redraw();
  }

  function onresponse(response) {
    if (JX.Device.getDevice() != 'desktop') {
      return;
    }

    var panel = JX.$(config.panelID);
    if (!response.xactions.length) {
      JX.DOM.hide(panel);
    } else {
      var preview_root = JX.$(config.timelineID);
      JX.DOM.setContent(
        preview_root,
        [
          JX.$H(response.header),
          JX.$H(response.xactions.join('')),
          JX.$H(response.previewContent)
        ]);
      JX.DOM.show(panel);

      // NOTE: Resonses are currently processed before associated behaviors are
      // registered. We need to defer invoking this event so that any behaviors
      // accompanying the response are registered.
      var invoke_preview = function() {
        JX.Stratcom.invoke(
          'EditEngine.didCommentPreview',
          null,
          {
            rootNode: preview_root
          });
      };
      setTimeout(invoke_preview, 0);
    }
  }

  function force_preview() {
    if (!config.showPreview) {
      return;
    }

    new JX.Request(config.actionURI, onresponse)
      .setData(get_data())
      .send();
  }

  function add_row(option) {
    var action = action_map[option.value];
    if (!action) {
      return;
    }

    // Remove any conflicting actions. For example, "Accept Revision" conflicts
    // with "Reject Revision".
    var conflict_key = action.conflictKey || null;
    if (conflict_key !== null) {
      for (var k in action_map) {
        if (k === action.key) {
          continue;
        }
        if (action_map[k].conflictKey !== conflict_key) {
          continue;
        }

        if (!(k in rows)) {
          continue;
        }

        remove_action(k);
      }
    }

    option.disabled = true;

    var aural = JX.$N('span', {className: 'aural-only'}, action.auralLabel);

    var icon = new JX.PHUIXIconView()
      .setIcon('fa-times-circle');

    var remove = JX.$N('a', {href: '#'}, [aural, icon.getNode()]);

    var control = new JX.PHUIXFormControl()
      .setLabel(action.label)
      .setError(remove)
      .setControl(action.type, action.spec)
      .setClass('phui-comment-action');
    var node = control.getNode();

    JX.Stratcom.addSigil(node, 'touchable');

    JX.DOM.listen(node, 'gesture.swipe.end', null, function(e) {
      var data = e.getData();

      if (data.direction != 'left') {
        // Didn't swipe left.
        return;
      }

      if (data.length <= (JX.Vector.getDim(node).x / 2)) {
        // Didn't swipe far enough.
        return;
      }

      remove_action(action.key);
    });

    rows[action.key] = {
      control: control,
      node: node,
      option: option
    };

    JX.DOM.listen(remove, 'click', null, function(e) {
      e.kill();
      remove_action(action.key);
    });

    place_node.parentNode.insertBefore(node, place_node);

    redraw();

    force_preview();

    return control;
  }

  JX.DOM.listen(form_node, ['submit', 'didSyntheticSubmit'], null, function() {
    input_node.value = serialize_actions();
  });

  if (config.showPreview) {
    var request = new JX.PhabricatorShapedRequest(
      config.actionURI,
      onresponse,
      get_data);

    var trigger = JX.bind(request, request.trigger);

    JX.DOM.listen(form_node, 'keydown', null, trigger);

    JX.DOM.listen(form_node, 'shouldRefresh', null, force_preview);
    request.start();

    var old_device = JX.Device.getDevice();

    var ondevicechange = function() {
      var new_device = JX.Device.getDevice();

      var panel = JX.$(config.panelID);
      if (new_device == 'desktop') {
        request.setRateLimit(500);

        // Force an immediate refresh if we switched from another device type
        // to desktop.
        if (old_device != new_device) {
          force_preview();
        }
      } else {
        // On mobile, don't show live previews and only save drafts every
        // 10 seconds.
        request.setRateLimit(10000);
        JX.DOM.hide(panel);
      }

      old_device = new_device;
    };

    ondevicechange();

    JX.Stratcom.listen('phabricator-device-change', null, ondevicechange);
  }

  restore_draft_actions(config.drafts || []);

});
