/* */ 
'use strict';
var $ = require('./$'),
    ctx = require('./$.ctx'),
    $export = require('./$.export'),
    createDesc = require('./$.property-desc'),
    assign = require('./$.object-assign'),
    keyOf = require('./$.keyof'),
    aFunction = require('./$.a-function'),
    forOf = require('./$.for-of'),
    isIterable = require('./core.is-iterable'),
    $iterCreate = require('./$.iter-create'),
    step = require('./$.iter-step'),
    isObject = require('./$.is-object'),
    toIObject = require('./$.to-iobject'),
    DESCRIPTORS = require('./$.descriptors'),
    has = require('./$.has'),
    getKeys = $.getKeys;
var createDictMethod = function(TYPE) {
  var IS_MAP = TYPE == 1,
      IS_EVERY = TYPE == 4;
  return function(object, callbackfn, that) {
    var f = ctx(callbackfn, that, 3),
        O = toIObject(object),
        result = IS_MAP || TYPE == 7 || TYPE == 2 ? new (typeof this == 'function' ? this : Dict) : undefined,
        key,
        val,
        res;
    for (key in O)
      if (has(O, key)) {
        val = O[key];
        res = f(val, key, object);
        if (TYPE) {
          if (IS_MAP)
            result[key] = res;
          else if (res)
            switch (TYPE) {
              case 2:
                result[key] = val;
                break;
              case 3:
                return true;
              case 5:
                return val;
              case 6:
                return key;
              case 7:
                result[res[0]] = res[1];
            }
          else if (IS_EVERY)
            return false;
        }
      }
    return TYPE == 3 || IS_EVERY ? IS_EVERY : result;
  };
};
var findKey = createDictMethod(6);
var createDictIter = function(kind) {
  return function(it) {
    return new DictIterator(it, kind);
  };
};
var DictIterator = function(iterated, kind) {
  this._t = toIObject(iterated);
  this._a = getKeys(iterated);
  this._i = 0;
  this._k = kind;
};
$iterCreate(DictIterator, 'Dict', function() {
  var that = this,
      O = that._t,
      keys = that._a,
      kind = that._k,
      key;
  do {
    if (that._i >= keys.length) {
      that._t = undefined;
      return step(1);
    }
  } while (!has(O, key = keys[that._i++]));
  if (kind == 'keys')
    return step(0, key);
  if (kind == 'values')
    return step(0, O[key]);
  return step(0, [key, O[key]]);
});
function Dict(iterable) {
  var dict = $.create(null);
  if (iterable != undefined) {
    if (isIterable(iterable)) {
      forOf(iterable, true, function(key, value) {
        dict[key] = value;
      });
    } else
      assign(dict, iterable);
  }
  return dict;
}
Dict.prototype = null;
function reduce(object, mapfn, init) {
  aFunction(mapfn);
  var O = toIObject(object),
      keys = getKeys(O),
      length = keys.length,
      i = 0,
      memo,
      key;
  if (arguments.length < 3) {
    if (!length)
      throw TypeError('Reduce of empty object with no initial value');
    memo = O[keys[i++]];
  } else
    memo = Object(init);
  while (length > i)
    if (has(O, key = keys[i++])) {
      memo = mapfn(memo, O[key], key, object);
    }
  return memo;
}
function includes(object, el) {
  return (el == el ? keyOf(object, el) : findKey(object, function(it) {
    return it != it;
  })) !== undefined;
}
function get(object, key) {
  if (has(object, key))
    return object[key];
}
function set(object, key, value) {
  if (DESCRIPTORS && key in Object)
    $.setDesc(object, key, createDesc(0, value));
  else
    object[key] = value;
  return object;
}
function isDict(it) {
  return isObject(it) && $.getProto(it) === Dict.prototype;
}
$export($export.G + $export.F, {Dict: Dict});
$export($export.S, 'Dict', {
  keys: createDictIter('keys'),
  values: createDictIter('values'),
  entries: createDictIter('entries'),
  forEach: createDictMethod(0),
  map: createDictMethod(1),
  filter: createDictMethod(2),
  some: createDictMethod(3),
  every: createDictMethod(4),
  find: createDictMethod(5),
  findKey: findKey,
  mapPairs: createDictMethod(7),
  reduce: reduce,
  keyOf: keyOf,
  includes: includes,
  has: has,
  get: get,
  set: set,
  isDict: isDict
});
