"use strict";

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

/**
 * Returns whether `node` is of given `type`.
 *
 * For better performance, use this instead of `is[Type]` when `type` is unknown.
 * Optionally, pass `skipAliasCheck` to directly compare `node.type` with `type`.
 */

exports.is = is;
exports.isType = isType;

/*
 * Description
 */

exports.shallowEqual = shallowEqual;

/**
 * Description
 */

exports.appendToMemberExpression = appendToMemberExpression;

/**
 * Description
 */

exports.prependToMemberExpression = prependToMemberExpression;

/**
 * Description
 */

exports.ensureBlock = ensureBlock;

/**
 * Description
 */

exports.clone = clone;

/**
 * Description
 */

exports.cloneDeep = cloneDeep;

/**
 * Build a function that when called will return whether or not the
 * input `node` `MemberExpression` matches the input `match`.
 *
 * For example, given the match `React.createClass` it would match the
 * parsed nodes of `React.createClass` and `React["createClass"]`.
 */

exports.buildMatchMemberExpression = buildMatchMemberExpression;

/**
 * Description
 */

exports.removeComments = removeComments;

/**
 * Description
 */

exports.inheritsComments = inheritsComments;

/**
 * Description
 */

exports.inherits = inherits;
exports.__esModule = true;

var toFastProperties = _interopRequire(require("to-fast-properties"));

var compact = _interopRequire(require("lodash/array/compact"));

var assign = _interopRequire(require("lodash/object/assign"));

var each = _interopRequire(require("lodash/collection/each"));

var uniq = _interopRequire(require("lodash/array/uniq"));

var t = exports;

/**
 * Registers `is[Type]` and `assert[Type]` generated functions for a given `type`.
 * Pass `skipAliasCheck` to force it to directly compare `node.type` with `type`.
 */

function registerType(type, skipAliasCheck) {
  var is = t["is" + type] = function (node, opts) {
    return t.is(type, node, opts, skipAliasCheck);
  };

  t["assert" + type] = function (node, opts) {
    if (!opts) opts = {};

    if (!is(node, opts)) {
      throw new Error("Expected type " + JSON.stringify(type) + " with option " + JSON.stringify(opts));
    }
  };
}

var STATEMENT_OR_BLOCK_KEYS = ["consequent", "body", "alternate"];
exports.STATEMENT_OR_BLOCK_KEYS = STATEMENT_OR_BLOCK_KEYS;
var NATIVE_TYPE_NAMES = ["Array", "ArrayBuffer", "Boolean", "DataView", "Date", "Error", "EvalError", "Float32Array", "Float64Array", "Function", "Int8Array", "Int16Array", "Int32Array", "Map", "Number", "Object", "Proxy", "Promise", "RangeError", "ReferenceError", "RegExp", "Set", "String", "Symbol", "SyntaxError", "TypeError", "Uint8Array", "Uint8ClampedArray", "Uint16Array", "Uint32Array", "URIError", "WeakMap", "WeakSet"];
exports.NATIVE_TYPE_NAMES = NATIVE_TYPE_NAMES;
var FLATTENABLE_KEYS = ["body", "expressions"];
exports.FLATTENABLE_KEYS = FLATTENABLE_KEYS;
var FOR_INIT_KEYS = ["left", "init"];
exports.FOR_INIT_KEYS = FOR_INIT_KEYS;
var COMMENT_KEYS = ["leadingComments", "trailingComments"];

exports.COMMENT_KEYS = COMMENT_KEYS;
var VISITOR_KEYS = require("./visitor-keys");
exports.VISITOR_KEYS = VISITOR_KEYS;
var BUILDER_KEYS = require("./builder-keys");
exports.BUILDER_KEYS = BUILDER_KEYS;
var ALIAS_KEYS = require("./alias-keys");

exports.ALIAS_KEYS = ALIAS_KEYS;
t.FLIPPED_ALIAS_KEYS = {};

each(t.VISITOR_KEYS, function (keys, type) {
  registerType(type, true);
});

each(t.ALIAS_KEYS, function (aliases, type) {
  each(aliases, function (alias) {
    var _t$FLIPPED_ALIAS_KEYS, _alias;

    var types = (_t$FLIPPED_ALIAS_KEYS = t.FLIPPED_ALIAS_KEYS, _alias = alias, !_t$FLIPPED_ALIAS_KEYS[_alias] && (_t$FLIPPED_ALIAS_KEYS[_alias] = []), _t$FLIPPED_ALIAS_KEYS[_alias]);
    types.push(type);
  });
});

each(t.FLIPPED_ALIAS_KEYS, function (types, type) {
  t[type.toUpperCase() + "_TYPES"] = types;
  registerType(type, false);
});

var TYPES = Object.keys(t.VISITOR_KEYS).concat(Object.keys(t.FLIPPED_ALIAS_KEYS));exports.TYPES = TYPES;

function is(type, node, opts, skipAliasCheck) {
  if (!node) return false;

  var matches = isType(node.type, type);
  if (!matches) return false;

  if (typeof opts === "undefined") {
    return true;
  } else {
    return t.shallowEqual(node, opts);
  }
}

function isType(nodeType, targetType) {
  if (nodeType === targetType) return true;

  var aliases = t.FLIPPED_ALIAS_KEYS[targetType];
  if (aliases) return aliases.indexOf(nodeType) > -1;

  return false;
}

each(t.VISITOR_KEYS, function (keys, type) {
  if (t.BUILDER_KEYS[type]) return;

  var defs = {};
  each(keys, function (key) {
    defs[key] = null;
  });
  t.BUILDER_KEYS[type] = defs;
});

each(t.BUILDER_KEYS, function (keys, type) {
  t[type[0].toLowerCase() + type.slice(1)] = function () {
    var node = {};
    node.start = null;
    node.type = type;

    var i = 0;

    for (var key in keys) {
      var arg = arguments[i++];
      if (arg === undefined) arg = keys[key];
      node[key] = arg;
    }

    return node;
  };
});
function shallowEqual(actual, expected) {
  var keys = Object.keys(expected);

  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];

    if (actual[key] !== expected[key]) {
      return false;
    }
  }

  return true;
}

function appendToMemberExpression(member, append, computed) {
  member.object = t.memberExpression(member.object, member.property, member.computed);
  member.property = append;
  member.computed = !!computed;
  return member;
}

function prependToMemberExpression(member, append) {
  member.object = t.memberExpression(append, member.object);
  return member;
}

function ensureBlock(node) {
  var key = arguments[1] === undefined ? "body" : arguments[1];

  return node[key] = t.toBlock(node[key], node);
}

function clone(node) {
  var newNode = {};
  for (var key in node) {
    if (key[0] === "_") continue;
    newNode[key] = node[key];
  }
  return newNode;
}

function cloneDeep(node) {
  var newNode = {};

  for (var key in node) {
    if (key[0] === "_") continue;

    var val = node[key];

    if (val) {
      if (val.type) {
        val = t.cloneDeep(val);
      } else if (Array.isArray(val)) {
        val = val.map(t.cloneDeep);
      }
    }

    newNode[key] = val;
  }

  return newNode;
}

function buildMatchMemberExpression(match, allowPartial) {
  var parts = match.split(".");

  return function (member) {
    // not a member expression
    if (!t.isMemberExpression(member)) return false;

    var search = [member];
    var i = 0;

    while (search.length) {
      var node = search.shift();

      if (allowPartial && i === parts.length) {
        return true;
      }

      if (t.isIdentifier(node)) {
        // this part doesn't match
        if (parts[i] !== node.name) return false;
      } else if (t.isLiteral(node)) {
        // this part doesn't match
        if (parts[i] !== node.value) return false;
      } else if (t.isMemberExpression(node)) {
        if (node.computed && !t.isLiteral(node.property)) {
          // we can't deal with this
          return false;
        } else {
          search.push(node.object);
          search.push(node.property);
          continue;
        }
      } else {
        // we can't deal with this
        return false;
      }

      // too many parts
      if (++i > parts.length) {
        return false;
      }
    }

    return true;
  };
}

function removeComments(child) {
  each(COMMENT_KEYS, function (key) {
    delete child[key];
  });
  return child;
}

function inheritsComments(child, parent) {
  if (child && parent) {
    each(COMMENT_KEYS, function (key) {
      child[key] = uniq(compact([].concat(child[key], parent[key])));
    });
  }
  return child;
}

function inherits(child, parent) {
  if (!child || !parent) return child;

  child._declarations = parent._declarations;
  child._scopeInfo = parent._scopeInfo;
  child.range = parent.range;
  child.start = parent.start;
  child.loc = parent.loc;
  child.end = parent.end;

  child.typeAnnotation = parent.typeAnnotation;
  child.returnType = parent.returnType;

  t.inheritsComments(child, parent);
  return child;
}

toFastProperties(t);
toFastProperties(t.VISITOR_KEYS);

exports.__esModule = true;
assign(t, require("./retrievers"));
assign(t, require("./validators"));
assign(t, require("./converters"));