/* eslint no-eq-null: 0, eqeqeq: 0, no-unused-vars: 0 */ "use strict"; var customError = require("es5-ext/error/custom") , defineLength = require("es5-ext/function/_define-length") , d = require("d") , ee = require("event-emitter").methods , resolveResolve = require("./resolve-resolve") , resolveNormalize = require("./resolve-normalize"); var apply = Function.prototype.apply , call = Function.prototype.call , create = Object.create , defineProperties = Object.defineProperties , on = ee.on , emit = ee.emit; module.exports = function (original, length, options) { var cache = create(null) , conf , memLength , get , set , del , clear , extDel , extGet , extHas , normalizer , getListeners , setListeners , deleteListeners , memoized , resolve; if (length !== false) memLength = length; else if (isNaN(original.length)) memLength = 1; else memLength = original.length; if (options.normalizer) { normalizer = resolveNormalize(options.normalizer); get = normalizer.get; set = normalizer.set; del = normalizer.delete; clear = normalizer.clear; } if (options.resolvers != null) resolve = resolveResolve(options.resolvers); if (get) { memoized = defineLength(function (arg) { var id, result, args = arguments; if (resolve) args = resolve(args); id = get(args); if (id !== null) { if (hasOwnProperty.call(cache, id)) { if (getListeners) conf.emit("get", id, args, this); return cache[id]; } } if (args.length === 1) result = call.call(original, this, args[0]); else result = apply.call(original, this, args); if (id === null) { id = get(args); if (id !== null) throw customError("Circular invocation", "CIRCULAR_INVOCATION"); id = set(args); } else if (hasOwnProperty.call(cache, id)) { throw customError("Circular invocation", "CIRCULAR_INVOCATION"); } cache[id] = result; if (setListeners) conf.emit("set", id, null, result); return result; }, memLength); } else if (length === 0) { memoized = function () { var result; if (hasOwnProperty.call(cache, "data")) { if (getListeners) conf.emit("get", "data", arguments, this); return cache.data; } if (arguments.length) result = apply.call(original, this, arguments); else result = call.call(original, this); if (hasOwnProperty.call(cache, "data")) { throw customError("Circular invocation", "CIRCULAR_INVOCATION"); } cache.data = result; if (setListeners) conf.emit("set", "data", null, result); return result; }; } else { memoized = function (arg) { var result, args = arguments, id; if (resolve) args = resolve(arguments); id = String(args[0]); if (hasOwnProperty.call(cache, id)) { if (getListeners) conf.emit("get", id, args, this); return cache[id]; } if (args.length === 1) result = call.call(original, this, args[0]); else result = apply.call(original, this, args); if (hasOwnProperty.call(cache, id)) { throw customError("Circular invocation", "CIRCULAR_INVOCATION"); } cache[id] = result; if (setListeners) conf.emit("set", id, null, result); return result; }; } conf = { original: original, memoized: memoized, profileName: options.profileName, get: function (args) { if (resolve) args = resolve(args); if (get) return get(args); return String(args[0]); }, has: function (id) { return hasOwnProperty.call(cache, id); }, delete: function (id) { var result; if (!hasOwnProperty.call(cache, id)) return; if (del) del(id); result = cache[id]; delete cache[id]; if (deleteListeners) conf.emit("delete", id, result); }, clear: function () { var oldCache = cache; if (clear) clear(); cache = create(null); conf.emit("clear", oldCache); }, on: function (type, listener) { if (type === "get") getListeners = true; else if (type === "set") setListeners = true; else if (type === "delete") deleteListeners = true; return on.call(this, type, listener); }, emit: emit, updateEnv: function () { original = conf.original; } }; if (get) { extDel = defineLength(function (arg) { var id, args = arguments; if (resolve) args = resolve(args); id = get(args); if (id === null) return; conf.delete(id); }, memLength); } else if (length === 0) { extDel = function () { return conf.delete("data"); }; } else { extDel = function (arg) { if (resolve) arg = resolve(arguments)[0]; return conf.delete(arg); }; } extGet = defineLength(function () { var id, args = arguments; if (length === 0) return cache.data; if (resolve) args = resolve(args); if (get) id = get(args); else id = String(args[0]); return cache[id]; }); extHas = defineLength(function () { var id, args = arguments; if (length === 0) return conf.has("data"); if (resolve) args = resolve(args); if (get) id = get(args); else id = String(args[0]); if (id === null) return false; return conf.has(id); }); defineProperties(memoized, { __memoized__: d(true), delete: d(extDel), clear: d(conf.clear), _get: d(extGet), _has: d(extHas) }); return conf; };