import { hasOwnProperty } from './hasOwn';
var toString = Object.prototype.toString;
var valueOfSymbol = Symbol.prototype.valueOf;
var objKeys = Object.keys;
var objIs = Object.is;
export default function isEqual(value, other) {
    var stack = [{ type: 'generic', a: value, b: other }];
    var aStack = [];
    var bStack = [];
    while (stack.length > 0) {
        var op = stack.pop();
        if (op.type === 'generic') {
            var a = op.a, b = op.b;
            if (objIs(a, b)) {
                continue;
            }
            var type = typeof a;
            if (type !== 'function' && type !== 'object' && typeof b !== 'object') {
                return false;
            }
            var tag = toString.call(a);
            if (tag !== toString.call(b)) {
                return false;
            }
            if (tag === '[object RegExp]' || tag === '[object String]') {
                if ('' + a !== '' + b) {
                    return false;
                }
                continue;
            }
            if (tag === '[object Number]') {
                if (!objIs(+a, +b)) {
                    return false;
                }
                continue;
            }
            if (tag === '[object Date]' || tag === '[object Boolean]') {
                if (+a !== +b) {
                    return false;
                }
                continue;
            }
            if (tag === '[object Symbol]') {
                if (valueOfSymbol.call(a) !== valueOfSymbol.call(b)) {
                    return false;
                }
                continue;
            }
            var areArrays = tag === '[object Array]';
            if (!areArrays) {
                if (typeof a !== 'object' || typeof b !== 'object') {
                    return false;
                }
                var aCtor = a.constructor;
                var bCtor = b.constructor;
                if (aCtor !== bCtor &&
                    !(typeof aCtor === 'function' &&
                        aCtor instanceof aCtor &&
                        typeof bCtor === 'function' &&
                        bCtor instanceof bCtor) &&
                    'constructor' in a &&
                    'constructor' in b) {
                    return false;
                }
            }
            var length_1 = aStack.length;
            while (length_1--) {
                if (aStack[length_1] === a) {
                    if (bStack[length_1] === b) {
                        break;
                    }
                    return false;
                }
            }
            if (length_1 >= 0) {
                continue;
            }
            aStack.push(a);
            bStack.push(b);
            stack.push({
                type: 'children-done',
            });
            if (areArrays) {
                var len = a.length;
                if (len !== b.length) {
                    return false;
                }
                if (len === 0) {
                    continue;
                }
                stack.push({
                    type: 'array',
                    size: len,
                    index: 0,
                    a: a,
                    b: b,
                });
            }
            else if (tag === '[object Object]') {
                var keys = objKeys(a);
                var len = keys.length;
                if (objKeys(b).length !== len) {
                    return false;
                }
                if (len === 0) {
                    continue;
                }
                stack.push({
                    type: 'object',
                    a: a,
                    b: b,
                    keys: keys,
                    size: len,
                    index: 0,
                });
            }
            else {
                throw new Error("isEqual not implemented for " + tag);
            }
        }
        else if (op.type === 'array') {
            var a = op.a, b = op.b, size = op.size, index = op.index;
            if (index < size - 1) {
                op.index++;
                stack.push(op);
            }
            stack.push({
                type: 'generic',
                a: a[index],
                b: b[index],
            });
        }
        else if (op.type === 'object') {
            var a = op.a, b = op.b, keys = op.keys, size = op.size, index = op.index;
            var k = keys[index];
            if (!hasOwnProperty(b, k)) {
                return false;
            }
            if (index < size - 1) {
                op.index++;
                stack.push(op);
            }
            stack.push({
                type: 'generic',
                a: a[k],
                b: b[k],
            });
        }
        else if (op.type === 'children-done') {
            aStack.pop();
            bStack.pop();
        }
    }
    return true;
}
