'use strict';

var traverse = require('../../');

function toS(o) {
	return Object.prototype.toString.call(o);
}

module.exports = function (a, b) {
	if (arguments.length !== 2) {
		throw new Error('deepEqual requires exactly two objects to compare against');
	}

	var equal = true;
	function notEqual() {
		equal = false;
		// this.stop();
		return undefined;
	}

	var node = b;

	traverse(a).forEach(function (y) { // eslint-disable-line consistent-return

		// if (node === undefined || node === null) return notEqual();

		if (!this.isRoot) {
			/*
            if (!Object.hasOwnProperty.call(node, this.key)) {
                return notEqual();
            }
        */
			if (typeof node !== 'object') { return notEqual(); }
			node = node[this.key];
		}

		var x = node;

		this.post(function () {
			node = x;
		});

		if (this.circular) {
			if (traverse(b).get(this.circular.path) !== x) { notEqual(); }
		} else if (typeof x !== typeof y) {
			notEqual();
		} else if (x === null || y === null || x === undefined || y === undefined) {
			if (x !== y) { notEqual(); }
		} else if (x.__proto__ !== y.__proto__) {
			notEqual();
		} else if (x === y) {
			// nop
		} else if (typeof x === 'function') {
			if (x instanceof RegExp) {
				// both regexps on account of the __proto__ check
				if (String(x) !== String(y)) { notEqual(); }
			} else if (x !== y) { notEqual(); }
		} else if (typeof x === 'object') {
			if (toS(y) === '[object Arguments]'
            || toS(x) === '[object Arguments]') {
				if (toS(x) !== toS(y)) {
					notEqual();
				}
			} else if (toS(y) === '[object RegExp]'
            || toS(x) === '[object RegExp]') {
				if (!x || !y || x.toString() !== y.toString()) { notEqual(); }
			} else if (x instanceof Date || y instanceof Date) {
				if (!(x instanceof Date) || !(y instanceof Date)
                || x.getTime() !== y.getTime()) {
					notEqual();
				}
			} else {
				var kx = Object.keys(x);
				var ky = Object.keys(y);
				if (kx.length !== ky.length) { return notEqual(); }
				for (var i = 0; i < kx.length; i++) {
					var k = kx[i];
					if (!Object.hasOwnProperty.call(y, k)) {
						notEqual();
					}
				}
			}
		}
	});

	return equal;
};
