'use strict';
var each = require('./_array-methods')(0)
    , redefine = require('./_redefine')
    , meta = require('./_meta')
    , assign = require('./_object-assign')
    , weak = require('./_collection-weak')
    , isObject = require('./_is-object')
    , getWeak = meta.getWeak
    , isExtensible = Object.isExtensible
    , uncaughtFrozenStore = weak.ufstore
    , tmp = {}
    , InternalMap;

var wrapper = function (get) {
    return function WeakMap() {
        return get(this, arguments.length > 0 ? arguments[0] : undefined);
    };
};

var methods = {
    // 23.3.3.3 WeakMap.prototype.get(key)
    get: function get(key) {
        if (isObject(key)) {
            var data = getWeak(key);
            if (data === true)return uncaughtFrozenStore(this).get(key);
            return data ? data[this._i] : undefined;
        }
    },
    // 23.3.3.5 WeakMap.prototype.set(key, value)
    set: function set(key, value) {
        return weak.def(this, key, value);
    }
};

// 23.3 WeakMap Objects
var $WeakMap = module.exports = require('./_collection')('WeakMap', wrapper, methods, weak, true, true);

// IE11 WeakMap frozen keys fix
if (new $WeakMap().set((Object.freeze || Object)(tmp), 7).get(tmp) != 7) {
    InternalMap = weak.getConstructor(wrapper);
    assign(InternalMap.prototype, methods);
    meta.NEED = true;
    each(['delete', 'has', 'get', 'set'], function (key) {
        var proto = $WeakMap.prototype
            , method = proto[key];
        redefine(proto, key, function (a, b) {
            // store frozen objects on internal weakmap shim
            if (isObject(a) && !isExtensible(a)) {
                if (!this._f)this._f = new InternalMap;
                var result = this._f[key](a, b);
                return key == 'set' ? this : result;
                // store all the rest on native weakmap
            }
            return method.call(this, a, b);
        });
    });
}