"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.MemoMap = exports.LayerZipWithSeq = exports.LayerZipWithPar = exports.LayerSuspend = exports.LayerMap = exports.LayerManaged = exports.LayerFresh = exports.LayerFold = exports.LayerChain = exports.LayerAllSeq = exports.LayerAllPar = exports.Layer = exports.Empty = void 0;
exports.and = and;
exports.and_ = and_;
exports.build = build;
exports.compose = compose;
exports.compose_ = compose_;
exports.fold = fold;
exports.fromRawEffect = fromRawEffect;
exports.fromRawFunction = fromRawFunction;
exports.fromRawFunctionM = fromRawFunctionM;
exports.fromRawManaged = fromRawManaged;
exports.hashSym = void 0;
exports.identity = identity;
exports.makeMemoMap = makeMemoMap;
exports.provideLayer = provideLayer;
exports.provideLayer_ = provideLayer_;
exports.provideSomeLayer = provideSomeLayer;
exports.provideSomeLayer_ = provideSomeLayer_;
exports.scope = scope;
exports.using = using;
exports.usingAnd = usingAnd;

var _reduce = /*#__PURE__*/require("../Collections/Immutable/Chunk/api/reduce.cjs");

var _index = /*#__PURE__*/require("../Collections/Immutable/Map/index.cjs");

var Tp = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Collections/Immutable/Tuple/index.cjs"));

var _commons = /*#__PURE__*/require("../Effect/commons.cjs");

var _ExecutionStrategy = /*#__PURE__*/require("../Effect/ExecutionStrategy.cjs");

var _index3 = /*#__PURE__*/require("../Function/index.cjs");

var _core = /*#__PURE__*/require("../Managed/core.cjs");

var _do = /*#__PURE__*/require("../Managed/do.cjs");

var _forEach = /*#__PURE__*/require("../Managed/forEach.cjs");

var _fromEffect = /*#__PURE__*/require("../Managed/fromEffect.cjs");

var _managed = /*#__PURE__*/require("../Managed/managed.cjs");

var _environment = /*#__PURE__*/require("../Managed/methods/environment.cjs");

var add = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Managed/ReleaseMap/add.cjs"));

var Finalizer = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Managed/ReleaseMap/finalizer.cjs"));

var makeReleaseMap = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Managed/ReleaseMap/makeReleaseMap.cjs"));

var releaseAll = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Managed/ReleaseMap/releaseAll.cjs"));

var _succeed = /*#__PURE__*/require("../Managed/succeed.cjs");

var _use = /*#__PURE__*/require("../Managed/use.cjs");

var _await = /*#__PURE__*/require("../Promise/await.cjs");

var _halt = /*#__PURE__*/require("../Promise/halt.cjs");

var _make = /*#__PURE__*/require("../Promise/make.cjs");

var _succeed2 = /*#__PURE__*/require("../Promise/succeed.cjs");

var R = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../Ref/index.cjs"));

var RM = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("../RefM/index.cjs"));

var _index6 = /*#__PURE__*/require("../Support/AtomicReference/index.cjs");

var T = /*#__PURE__*/_interopRequireWildcard( /*#__PURE__*/require("./deps-effect.cjs"));

function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }

function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }

var _a;

/**
 * Creates a layer from an effect
 */
function fromRawEffect(resource) {
  return new LayerManaged((0, _fromEffect.fromEffect)(resource));
}
/**
 * Creates a layer from a function
 */


function fromRawFunction(f) {
  return fromRawEffect(T.access(f));
}
/**
 * Creates a layer from an effectful function
 */


function fromRawFunctionM(f) {
  return fromRawEffect(T.accessM(f));
}
/**
 * Creates a layer from a managed environment
 */


function fromRawManaged(resource) {
  return new LayerManaged(resource);
}
/**
 * Constructs a layer that passes along the specified environment as an
 * output.
 */


function identity() {
  return fromRawManaged((0, _environment.environment)());
}
/**
 * Merge two Layers in parallel without providing any data to each other
 *
 * @param self - first Layer to combine
 * @param that - second Layer to combine
 */


function and_(self, that) {
  return new LayerZipWithPar(self, that, (l, r) => ({ ...l,
    ...r
  }));
}
/**
 * Merge two Layers in parallel without providing any data to each other
 *
 * @param that - second Layer to combine
 * @param self - first Layer to combine
 */


function and(that) {
  return self => new LayerZipWithPar(self, that, (l, r) => ({ ...l,
    ...r
  }));
}
/**
 * Feeds the error or output services of this layer into the input of either
 * the specified `failure` or `success` layers, resulting in a new layer with
 * the inputs of this layer, and the error or outputs of the specified layer.
 */


function fold(self) {
  return failure => success => new LayerFold(self, failure, success);
}
/**
 * Use `from` to partially provide environment into `to`
 */


function using(from) {
  return to => compose_(from["+++"](identity()), to);
}
/**
 * Use `from` to partially provide environment into `to` and merge both
 */


function usingAnd(from) {
  return to => compose_(from["+++"](identity()), to["+++"](identity()));
}
/**
 * Compose layers
 */


function compose_(from, to) {
  return fold(from)(fromRawFunctionM(_ => T.halt(_.get(1))))(to);
}
/**
 * Compose layers
 */


function compose(to) {
  return from => compose_(from, to);
}

const hashSym = /*#__PURE__*/Symbol();
exports.hashSym = hashSym;

class Layer {
  constructor() {
    this[_a] = new _index6.AtomicReference(Symbol());
  }
  /**
   * Set the hash key for memoization
   */


  setKey(hash) {
    this[hashSym].set(hash);
    return this;
  }

  [(_a = hashSym, "_I")]() {
    return this;
  }
  /**
   * Use that Layer to provide data to this
   */


  ["<=<"](that) {
    return that[">=>"](this);
  }
  /**
   * Use this Layer to provide data to that
   */


  [">=>"](that) {
    return compose_(this, that);
  }
  /**
   * Use that Layer to partially provide data to this
   */


  ["<<<"](that) {
    return that[">>>"](this);
  }

  [">>>"](that) {
    return this["+++"](identity())[">=>"](that);
  }
  /**
   * Create a Layer with the data from both Layers, while providing the data from this to that
   */


  [">+>"](that) {
    return this[">>>"](that["+++"](identity()));
  }
  /**
   * Create a Layer with the data from both Layers, while providing the data from that to this
   */


  ["<+<"](that) {
    return that[">+>"](this);
  }
  /**
   * Combine both layers in parallel
   */


  ["+++"](from) {
    return and_(from, this);
  }
  /**
   * Use the layer to provide partial environment to an effect
   */


  use(effect) {
    return provideSomeLayer(this)(effect);
  }
  /**
   * Use the layer to provide the full environment to an effect
   */


  useAll(effect) {
    return provideLayer(this)(effect);
  }
  /**
   * Use the layer to provide the full environment to an effect
   */


  get useForever() {
    return provideLayer(this)(T.never);
  }

}
/**
 * Provides a layer to the given effect
 */


exports.Layer = Layer;

function provideSomeLayer(layer) {
  return self => provideLayer_(self, layer["+++"](identity()));
}
/**
 * Provides a layer to the given effect
 */


function provideSomeLayer_(self, layer) {
  return provideLayer_(self, layer["+++"](identity()));
}
/**
 * Provides a layer to the given effect
 */


function provideLayer_(self, layer) {
  return (0, _use.use_)(build(layer), p => T.provideAll_(self, p));
}
/**
 * Provides a layer to the given effect
 */


function provideLayer(layer) {
  return self => provideLayer_(self, layer);
}

class LayerFold extends Layer {
  constructor(self, failure, success) {
    super();
    this.self = self;
    this.failure = failure;
    this.success = success;
    this._tag = "LayerFold";
  }

}

exports.LayerFold = LayerFold;

class LayerMap extends Layer {
  constructor(self, f) {
    super();
    this.self = self;
    this.f = f;
    this._tag = "LayerMap";
  }

}

exports.LayerMap = LayerMap;

class LayerChain extends Layer {
  constructor(self, f) {
    super();
    this.self = self;
    this.f = f;
    this._tag = "LayerChain";
  }

}

exports.LayerChain = LayerChain;

class LayerFresh extends Layer {
  constructor(self) {
    super();
    this.self = self;
    this._tag = "LayerFresh";
  }

}

exports.LayerFresh = LayerFresh;

class LayerManaged extends Layer {
  constructor(self) {
    super();
    this.self = self;
    this._tag = "LayerManaged";
  }

}

exports.LayerManaged = LayerManaged;

class LayerSuspend extends Layer {
  constructor(self) {
    super();
    this.self = self;
    this._tag = "LayerSuspend";
  }

}

exports.LayerSuspend = LayerSuspend;

class LayerZipWithPar extends Layer {
  constructor(self, that, f) {
    super();
    this.self = self;
    this.that = that;
    this.f = f;
    this._tag = "LayerZipWithPar";
  }

}

exports.LayerZipWithPar = LayerZipWithPar;

class LayerAllPar extends Layer {
  constructor(layers) {
    super();
    this.layers = layers;
    this._tag = "LayerAllPar";
  }

}

exports.LayerAllPar = LayerAllPar;

class LayerAllSeq extends Layer {
  constructor(layers) {
    super();
    this.layers = layers;
    this._tag = "LayerAllSeq";
  }

}

exports.LayerAllSeq = LayerAllSeq;

class LayerZipWithSeq extends Layer {
  constructor(self, that, f) {
    super();
    this.self = self;
    this.that = that;
    this.f = f;
    this._tag = "LayerZipWithSeq";
  }

}

exports.LayerZipWithSeq = LayerZipWithSeq;

function scope(_) {
  const I = _._I();

  switch (I._tag) {
    case "LayerFresh":
      {
        return (0, _succeed.succeed)(() => build(I.self));
      }

    case "LayerManaged":
      {
        return (0, _succeed.succeed)(() => I.self);
      }

    case "LayerSuspend":
      {
        return (0, _succeed.succeed)(memo => memo.getOrElseMemoize(I.self()));
      }

    case "LayerMap":
      {
        return (0, _succeed.succeed)(memo => (0, _core.map_)(memo.getOrElseMemoize(I.self), I.f));
      }

    case "LayerChain":
      {
        return (0, _succeed.succeed)(memo => (0, _core.chain_)(memo.getOrElseMemoize(I.self), a => memo.getOrElseMemoize(I.f(a))));
      }

    case "LayerZipWithPar":
      {
        return (0, _succeed.succeed)(memo => (0, _core.zipWithPar_)(memo.getOrElseMemoize(I.self), memo.getOrElseMemoize(I.that), I.f));
      }

    case "LayerZipWithSeq":
      {
        return (0, _succeed.succeed)(memo => (0, _core.zipWith_)(memo.getOrElseMemoize(I.self), memo.getOrElseMemoize(I.that), I.f));
      }

    case "LayerAllPar":
      {
        return (0, _succeed.succeed)(memo => {
          return (0, _core.map)((0, _reduce.reduce)({}, (b, a) => ({ ...b,
            ...a
          })))((0, _forEach.forEachPar_)(I.layers, memo.getOrElseMemoize));
        });
      }

    case "LayerAllSeq":
      {
        return (0, _succeed.succeed)(memo => {
          return (0, _core.map)((0, _reduce.reduce)({}, (b, a) => ({ ...b,
            ...a
          })))((0, _forEach.forEach_)(I.layers, memo.getOrElseMemoize));
        });
      }

    case "LayerFold":
      {
        return (0, _succeed.succeed)(memo => (0, _core.foldCauseM_)(memo.getOrElseMemoize(I.self), e => (0, _core.chain)(r => (0, _core.provideSome_)(memo.getOrElseMemoize(I.failure), () => Tp.tuple(r, e)))((0, _fromEffect.fromEffect)(T.environment())), r => (0, _core.provideAll_)(memo.getOrElseMemoize(I.success), r)));
      }
  }
}
/**
 * Builds a layer into a managed value.
 */


function build(_) {
  return (0, _core.map)(({
    value
  }) => value)((0, _do.bind)("value", ({
    memoMap,
    run
  }) => run(memoMap))((0, _do.bind)("run", () => scope(_))((0, _do.bind)("memoMap", () => (0, _fromEffect.fromEffect)(makeMemoMap()))(_do.do))));
}
/**
 * Creates a MemoMap
 */


function makeMemoMap() {
  return T.chain_(RM.makeRefM(new Map()), r => T.succeedWith(() => new MemoMap(r)));
}
/**
 * A `MemoMap` memoizes dependencies.
 */


class MemoMap {
  constructor(ref) {
    this.ref = ref;
    /**
     * Checks the memo map to see if a dependency exists. If it is, immediately
     * returns it. Otherwise, obtains the dependency, stores it in the memo map,
     * and adds a finalizer to the outer `Managed`.
     */

    this.getOrElseMemoize = layer => {
      return (0, _managed.managedApply)(T.flatten(RM.modify(m => {
        const inMap = m.get(layer[hashSym].get);

        if (inMap) {
          const {
            tuple: [acquire, release]
          } = inMap;
          const cached = T.accessM(({
            tuple: [_, rm]
          }) => T.map_(T.onExit_(acquire, ex => {
            switch (ex._tag) {
              case "Success":
                {
                  return add.add(release)(rm);
                }

              case "Failure":
                {
                  return T.unit;
                }
            }
          }), x => Tp.tuple(release, x)));
          return T.succeed(Tp.tuple(cached, m));
        } else {
          return T.map_(T.let_(T.let_(T.bind_(T.bind_(T.bind_(T.do, "observers", () => R.makeRef(0)), "promise", () => (0, _make.make)()), "finalizerRef", () => R.makeRef(Finalizer.noopFinalizer)), "resource", ({
            finalizerRef,
            observers,
            promise
          }) => T.uninterruptibleMask(({
            restore
          }) => T.map_(T.bind_(T.bind_(T.let_(T.let_(T.bind_(T.do, "env", () => T.environment()), "a", ({
            env: {
              tuple: [a]
            }
          }) => a), "outerReleaseMap", ({
            env: {
              tuple: [_, outerReleaseMap]
            }
          }) => outerReleaseMap), "innerReleaseMap", () => makeReleaseMap.makeReleaseMap), "tp", ({
            a,
            innerReleaseMap,
            outerReleaseMap
          }) => restore(T.chain_(T.result(T.provideAll_((0, _core.chain)(_ => _(this))(scope(layer)).effect, Tp.tuple(a, innerReleaseMap))), e => {
            switch (e._tag) {
              case "Failure":
                {
                  return T.chain_(T.chain_((0, _halt.halt)(e.cause)(promise), () => releaseAll.releaseAll(e, _ExecutionStrategy.sequential)(innerReleaseMap)), () => T.halt(e.cause));
                }

              case "Success":
                {
                  return T.map_(T.tap_(T.bind_(T.tap_(T.tap_(T.do, () => finalizerRef.set(e => T.whenM_(releaseAll.releaseAll(e, _ExecutionStrategy.sequential)(innerReleaseMap), R.modify_(observers, n => Tp.tuple(n === 1, n - 1))))), () => R.update_(observers, n => n + 1)), "outerFinalizer", () => add.add(e => T.chain_(finalizerRef.get, f => f(e)))(outerReleaseMap)), () => (0, _succeed2.succeed)(e.value.get(1))(promise)), ({
                    outerFinalizer
                  }) => Tp.tuple(outerFinalizer, e.value.get(1)));
                }
            }
          }))), ({
            tp
          }) => tp))), "memoized", ({
            finalizerRef,
            observers,
            promise
          }) => Tp.tuple(T.onExit_((0, _await.await)(promise), e => {
            switch (e._tag) {
              case "Failure":
                {
                  return T.unit;
                }

              case "Success":
                {
                  return R.update_(observers, n => n + 1);
                }
            }
          }), e => T.chain_(finalizerRef.get, f => f(e)))), ({
            memoized,
            resource
          }) => Tp.tuple(resource, (0, _index.insert)(layer[hashSym].get, memoized)(m)));
        }
      })(this.ref)));
    };
  }

}
/**
 * Empty layer, useful for init cases
 */


exports.MemoMap = MemoMap;
const Empty = /*#__PURE__*/new LayerSuspend(() => identity());
exports.Empty = Empty;
//# sourceMappingURL=definitions.cjs.map