/**
 * Copyright 2004-present Facebook. All Rights Reserved.
 */
/*global exports:true*/

/**
 * Implements ES6 call spread.
 *
 * instance.method(a, b, c, ...d)
 *
 * instance.method.apply(instance, [a, b, c].concat(d))
 *
 */

var Syntax = require('esprima-fb').Syntax;
var utils = require('../src/utils');

function process(traverse, node, path, state) {
  utils.move(node.range[0], state);
  traverse(node, path, state);
  utils.catchup(node.range[1], state);
}

function visitCallSpread(traverse, node, path, state) {
  utils.catchup(node.range[0], state);

  if (node.type === Syntax.NewExpression) {
    // Input  = new Set(1, 2, ...list)
    // Output = new (Function.prototype.bind.apply(Set, [null, 1, 2].concat(list)))
    utils.append('new (Function.prototype.bind.apply(', state);
    process(traverse, node.callee, path, state);
  } else if (node.callee.type === Syntax.MemberExpression) {
    // Input  = get().fn(1, 2, ...more)
    // Output = (_ = get()).fn.apply(_, [1, 2].apply(more))
    var tempVar = utils.injectTempVar(state);
    utils.append('(' + tempVar + ' = ', state);
    process(traverse, node.callee.object, path, state);
    utils.append(')', state);
    if (node.callee.property.type === Syntax.Identifier) {
      utils.append('.', state);
      process(traverse, node.callee.property, path, state);
    } else {
      utils.append('[', state);
      process(traverse, node.callee.property, path, state);
      utils.append(']', state);
    }
    utils.append('.apply(' + tempVar, state);
  } else {
    // Input  = max(1, 2, ...list)
    // Output = max.apply(null, [1, 2].concat(list))
    var needsToBeWrappedInParenthesis =
      node.callee.type === Syntax.FunctionDeclaration ||
      node.callee.type === Syntax.FunctionExpression;
    if (needsToBeWrappedInParenthesis) {
      utils.append('(', state);
    }
    process(traverse, node.callee, path, state);
    if (needsToBeWrappedInParenthesis) {
      utils.append(')', state);
    }
    utils.append('.apply(null', state);
  }
  utils.append(', ', state);

  var args = node.arguments.slice();
  var spread = args.pop();
  if (args.length || node.type === Syntax.NewExpression) {
    utils.append('[', state);
    if (node.type === Syntax.NewExpression) {
      utils.append('null' + (args.length ? ', ' : ''), state);
    }
    while (args.length) {
      var arg = args.shift();
      utils.move(arg.range[0], state);
      traverse(arg, path, state);
      if (args.length) {
        utils.catchup(args[0].range[0], state);
      } else {
        utils.catchup(arg.range[1], state);
      }
    }
    utils.append('].concat(', state);
    process(traverse, spread.argument, path, state);
    utils.append(')', state);
  } else {
    process(traverse, spread.argument, path, state);
  }
  utils.append(node.type === Syntax.NewExpression ? '))' : ')', state);

  utils.move(node.range[1], state);
  return false;
}

visitCallSpread.test = function(node, path, state) {
  return (
    (
      node.type === Syntax.CallExpression ||
      node.type === Syntax.NewExpression
    ) &&
    node.arguments.length > 0 &&
    node.arguments[node.arguments.length - 1].type === Syntax.SpreadElement
  );
};

exports.visitorList = [
  visitCallSpread,
];
