/* */ 
'use strict';
var $ = require("./$"),
    safe = require("./$.uid").safe,
    assert = require("./$.assert"),
    forOf = require("./$.for-of"),
    $has = $.has,
    isObject = $.isObject,
    hide = $.hide,
    isExtensible = Object.isExtensible || isObject,
    id = 0,
    ID = safe('id'),
    WEAK = safe('weak'),
    LEAK = safe('leak'),
    method = require("./$.array-methods"),
    find = method(5),
    findIndex = method(6);
function findFrozen(store, key) {
  return find(store.array, function(it) {
    return it[0] === key;
  });
}
function leakStore(that) {
  return that[LEAK] || hide(that, LEAK, {
    array: [],
    get: function(key) {
      var entry = findFrozen(this, key);
      if (entry)
        return entry[1];
    },
    has: function(key) {
      return !!findFrozen(this, key);
    },
    set: function(key, value) {
      var entry = findFrozen(this, key);
      if (entry)
        entry[1] = value;
      else
        this.array.push([key, value]);
    },
    'delete': function(key) {
      var index = findIndex(this.array, function(it) {
        return it[0] === key;
      });
      if (~index)
        this.array.splice(index, 1);
      return !!~index;
    }
  })[LEAK];
}
module.exports = {
  getConstructor: function(wrapper, NAME, IS_MAP, ADDER) {
    var C = wrapper(function(that, iterable) {
      $.set(assert.inst(that, C, NAME), ID, id++);
      if (iterable != undefined)
        forOf(iterable, IS_MAP, that[ADDER], that);
    });
    require("./$.mix")(C.prototype, {
      'delete': function(key) {
        if (!isObject(key))
          return false;
        if (!isExtensible(key))
          return leakStore(this)['delete'](key);
        return $has(key, WEAK) && $has(key[WEAK], this[ID]) && delete key[WEAK][this[ID]];
      },
      has: function has(key) {
        if (!isObject(key))
          return false;
        if (!isExtensible(key))
          return leakStore(this).has(key);
        return $has(key, WEAK) && $has(key[WEAK], this[ID]);
      }
    });
    return C;
  },
  def: function(that, key, value) {
    if (!isExtensible(assert.obj(key))) {
      leakStore(that).set(key, value);
    } else {
      $has(key, WEAK) || hide(key, WEAK, {});
      key[WEAK][that[ID]] = value;
    }
    return that;
  },
  leakStore: leakStore,
  WEAK: WEAK,
  ID: ID
};
