“use strict”;

Object.defineProperty(exports, “__esModule”, {

value: true

}); exports.makeStrongCache = makeStrongCache; exports.makeWeakCache = makeWeakCache; exports.assertSimpleType = assertSimpleType;

function makeStrongCache(handler) {

return makeCachedFunction(new Map(), handler);

}

function makeWeakCache(handler) {

return makeCachedFunction(new WeakMap(), handler);

}

function makeCachedFunction(callCache, handler) {

return function cachedFunction(arg, data) {
  let cachedValue = callCache.get(arg);

  if (cachedValue) {
    for (const _ref of cachedValue) {
      const {
        value,
        valid
      } = _ref;
      if (valid(data)) return value;
    }
  }

  const cache = new CacheConfigurator(data);
  const value = handler(arg, cache);
  if (!cache.configured()) cache.forever();
  cache.deactivate();

  switch (cache.mode()) {
    case "forever":
      cachedValue = [{
        value,
        valid: () => true
      }];
      callCache.set(arg, cachedValue);
      break;

    case "invalidate":
      cachedValue = [{
        value,
        valid: cache.validator()
      }];
      callCache.set(arg, cachedValue);
      break;

    case "valid":
      if (cachedValue) {
        cachedValue.push({
          value,
          valid: cache.validator()
        });
      } else {
        cachedValue = [{
          value,
          valid: cache.validator()
        }];
        callCache.set(arg, cachedValue);
      }

  }

  return value;
};

}

class CacheConfigurator {

constructor(data) {
  this._active = true;
  this._never = false;
  this._forever = false;
  this._invalidate = false;
  this._configured = false;
  this._pairs = [];
  this._data = data;
}

simple() {
  return makeSimpleConfigurator(this);
}

mode() {
  if (this._never) return "never";
  if (this._forever) return "forever";
  if (this._invalidate) return "invalidate";
  return "valid";
}

forever() {
  if (!this._active) {
    throw new Error("Cannot change caching after evaluation has completed.");
  }

  if (this._never) {
    throw new Error("Caching has already been configured with .never()");
  }

  this._forever = true;
  this._configured = true;
}

never() {
  if (!this._active) {
    throw new Error("Cannot change caching after evaluation has completed.");
  }

  if (this._forever) {
    throw new Error("Caching has already been configured with .forever()");
  }

  this._never = true;
  this._configured = true;
}

using(handler) {
  if (!this._active) {
    throw new Error("Cannot change caching after evaluation has completed.");
  }

  if (this._never || this._forever) {
    throw new Error("Caching has already been configured with .never or .forever()");
  }

  this._configured = true;
  const key = handler(this._data);

  this._pairs.push([key, handler]);

  return key;
}

invalidate(handler) {
  if (!this._active) {
    throw new Error("Cannot change caching after evaluation has completed.");
  }

  if (this._never || this._forever) {
    throw new Error("Caching has already been configured with .never or .forever()");
  }

  this._invalidate = true;
  this._configured = true;
  const key = handler(this._data);

  this._pairs.push([key, handler]);

  return key;
}

validator() {
  const pairs = this._pairs;
  return data => pairs.every(([key, fn]) => key === fn(data));
}

deactivate() {
  this._active = false;
}

configured() {
  return this._configured;
}

}

function makeSimpleConfigurator(cache) {

function cacheFn(val) {
  if (typeof val === "boolean") {
    if (val) cache.forever();else cache.never();
    return;
  }

  return cache.using(() => assertSimpleType(val()));
}

cacheFn.forever = () => cache.forever();

cacheFn.never = () => cache.never();

cacheFn.using = cb => cache.using(() => assertSimpleType(cb()));

cacheFn.invalidate = cb => cache.invalidate(() => assertSimpleType(cb()));

return cacheFn;

}

function assertSimpleType(value) {

if (value != null && typeof value !== "string" && typeof value !== "boolean" && typeof value !== "number") {
  throw new Error("Cache keys must be either string, boolean, number, null, or undefined.");
}

return value;

}