"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = void 0;

var _isArguments = _interopRequireDefault(require("./is-arguments"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

const pSlice = Array.prototype.slice;

const isUndefinedOrNull = value => {
  // eslint-disable-next-line no-undefined
  return value === null || value === undefined;
};

const isBuffer = x => {
  if (!x || typeof x !== 'object' || typeof x.length !== 'number') {
    return false;
  }

  if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
    return false;
  }

  if (x.length > 0 && typeof x[0] !== 'number') {
    return false;
  }

  return true;
};

const objEquiv = (a, b, opts) => {
  let i;
  let key;

  if (isUndefinedOrNull(a) || isUndefinedOrNull(b)) {
    return false;
  } // an identical 'prototype' property.


  if (a.prototype !== b.prototype) {
    return false;
  } // ~~~I've managed to break Object.keys through screwy arguments passing.
  //   Converting to array solves the problem.


  if ((0, _isArguments.default)(a)) {
    if (!(0, _isArguments.default)(b)) {
      return false;
    } // $FlowDisableInputArg


    a = pSlice.call(a); // $FlowDisableInputArg

    b = pSlice.call(b); // eslint-disable-next-line no-use-before-define

    return deepEqual(a, b, opts);
  }

  if (isBuffer(a)) {
    if (!isBuffer(b)) {
      return false;
    }

    if (a.length !== b.length) {
      return false;
    }

    for (i = 0; i < a.length; i++) {
      if (a[i] !== b[i]) {
        return false;
      }
    }

    return true;
  }

  let ka;
  let kb;

  try {
    ka = Object.keys(a);
    kb = Object.keys(b);
  } catch (e) {
    // happens when one is a string literal and the other isn't
    return false;
  } // having the same number of owned properties (keys incorporates
  // hasOwnProperty)
  // eslint-disable-next-line eqeqeq


  if (ka.length != kb.length) {
    return false;
  } // the same set of keys (although not necessarily the same order),


  ka.sort();
  kb.sort(); // ~~~cheap key test

  for (i = ka.length - 1; i >= 0; i--) {
    // eslint-disable-next-line eqeqeq
    if (ka[i] != kb[i]) {
      return false;
    }
  } // equivalent values for every corresponding key, and
  // ~~~possibly expensive deep test


  for (i = ka.length - 1; i >= 0; i--) {
    key = ka[i]; // eslint-disable-next-line no-use-before-define

    if (!deepEqual(a[key], b[key], opts)) {
      return false;
    }
  }

  return typeof a === typeof b;
};

const deepEqual = (actual, expected, opts = {}) => {
  // 7.1. All identical values are equivalent, as determined by ===.
  if (actual === expected) {
    return true;
  }

  if (actual instanceof Date && expected instanceof Date) {
    return actual.getTime() === expected.getTime();
  } // 7.3. Other pairs that do not both pass typeof value == 'object',
  // equivalence is determined by ==.


  if (!actual || !expected || typeof actual !== 'object' && typeof expected !== 'object') {
    // eslint-disable-next-line eqeqeq
    return opts.strict ? actual === expected : actual == expected;
  } // 7.4. For all other Object pairs, including Array objects, equivalence is
  // determined by having the same number of owned properties (as verified
  // with Object.prototype.hasOwnProperty.call), the same set of keys
  // (although not necessarily the same order), equivalent values for every
  // corresponding key, and an identical 'prototype' property. Note: this
  // accounts for both named and indexed properties on Arrays.


  return objEquiv(actual, expected, opts);
};

var _default = deepEqual;
exports.default = _default;