/**
 * @provides javelin-behavior-differential-keyboard-navigation
 * @requires javelin-behavior
 *           javelin-dom
 *           phabricator-keyboard-shortcut
 */

JX.behavior('differential-keyboard-navigation', function(config) {

  var cursor = -1;
  var changesets;

  var selection_begin = null;
  var selection_end = null;

  function init() {
    if (changesets) {
      return;
    }
    changesets = JX.DOM.scry(document.body, 'div', 'differential-changeset');
  }

  function getBlocks(cursor) {
    // TODO: This might not be terribly fast; we can't currently memoize it
    // because it can change as ajax requests come in (e.g., content loads).

    var rows = JX.DOM.scry(changesets[cursor], 'tr');
    var blocks = [[changesets[cursor], changesets[cursor]]];
    var start = null;
    var type;
    for (var ii = 0; ii < rows.length; ii++) {
      type = getRowType(rows[ii]);
      if (type == 'ignore') {
        continue;
      }
      if (!type && start) {
        blocks.push([start, rows[ii - 1]]);
        start = null;
      } else if (type && !start) {
        start = rows[ii];
      }
    }
    if (start) {
      blocks.push([start, rows[ii - 1]]);
    }

    return blocks;
  }

  function getRowType(row) {
    // NOTE: Being somewhat over-general here to allow other types of objects
    // to be easily focused in the future (inline comments, 'show more..').

    if (row.className.indexOf('inline') !== -1) {
      return 'ignore';
    }

    if (row.className.indexOf('differential-changeset') !== -1) {
      return 'file';
    }

    var cells = JX.DOM.scry(row, 'td');

    for (var ii = 0; ii < cells.length; ii++) {
      // NOTE: The semantic use of classnames here is for performance; don't
      // emulate this elsewhere since it's super terrible.
      if (cells[ii].className.indexOf('old') !== -1 ||
          cells[ii].className.indexOf('new') !== -1) {
        return 'change';
      }
    }

    return null;
  }

  function jump(manager, delta, jump_to_file) {
    init();

    if (cursor < 0) {
      if (delta < 0) {
        // If the user goes "back" without a selection, just reject the action.
        return;
      } else {
        cursor = 0;
      }
    }

    while (true) {
      var blocks = getBlocks(cursor);
      var focus;
      if (delta < 0) {
        focus = blocks.length;
      } else {
        focus = -1;
      }

      for (var ii = 0; ii < blocks.length; ii++) {
        if (blocks[ii][0] == selection_begin) {
          focus = ii;
          break;
        }
      }

      while (true) {
        focus += delta;

        if (blocks[focus]) {
          var row_type = getRowType(blocks[focus][0]);
          if (jump_to_file && row_type != 'file') {
            continue;
          }

          selection_begin = blocks[focus][0];
          selection_end = blocks[focus][1];

          manager.scrollTo(selection_begin);
          manager.focusOn(selection_begin, selection_end);

          return;
        } else {
          var adjusted = (cursor + delta);
          if (adjusted < 0 || adjusted >= changesets.length) {
            // Stop cursor movement when the user reaches either end.
            return;
          }
          cursor = adjusted;

          // Break the inner loop and go to the next file.
          break;
        }
      }
    }

  }

  var is_haunted = false;
  function haunt() {
    is_haunted = !is_haunted;
    var haunt = JX.$(config.haunt)
    JX.DOM.alterClass(haunt, 'differential-haunted-panel', is_haunted);
  }

  new JX.KeyboardShortcut('j', 'Jump to next change.')
    .setHandler(function(manager) {
      jump(manager, 1);
    })
    .register();

  new JX.KeyboardShortcut('k', 'Jump to previous change.')
    .setHandler(function(manager) {
      jump(manager, -1);
    })
    .register();

  new JX.KeyboardShortcut('J', 'Jump to next file.')
    .setHandler(function(manager) {
      jump(manager, 1, true);
    })
    .register();

  new JX.KeyboardShortcut('K', 'Jump to previous file.')
    .setHandler(function(manager) {
      jump(manager, -1, true);
    })
    .register();


  new JX.KeyboardShortcut('z', 'Haunt / unhaunt comment panel.')
    .setHandler(haunt)
    .register();

});