"use strict"
var __createBinding =
  (this && this.__createBinding) ||
  (Object.create
    ? function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        Object.defineProperty(o, k2, {
          enumerable: true,
          get: function () {
            return m[k]
          }
        })
      }
    : function (o, m, k, k2) {
        if (k2 === undefined) k2 = k
        o[k2] = m[k]
      })
var __setModuleDefault =
  (this && this.__setModuleDefault) ||
  (Object.create
    ? function (o, v) {
        Object.defineProperty(o, "default", { enumerable: true, value: v })
      }
    : function (o, v) {
        o["default"] = v
      })
var __importStar =
  (this && this.__importStar) ||
  function (mod) {
    if (mod && mod.__esModule) return mod
    var result = {}
    if (mod != null)
      for (var k in mod)
        if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k))
          __createBinding(result, mod, k)
    __setModuleDefault(result, mod)
    return result
  }
Object.defineProperty(exports, "__esModule", { value: true })
exports.fail =
  exports.foldM =
  exports.foldM_ =
  exports.chain =
  exports.chain_ =
  exports.forEachChunkWhile =
  exports.forEachChunk =
  exports.reduceM =
  exports.reduce =
  exports.forEach =
  exports.forEachWhile =
  exports.drain =
  exports.collectAll =
  exports.Sink =
    void 0
// tracing: off
require("../../../Operator")
const Chunk = __importStar(require("../../../Collections/Immutable/Chunk"))
const Tp = __importStar(require("../../../Collections/Immutable/Tuple"))
const T = __importStar(require("../../../Effect"))
const Function_1 = require("../../../Function")
const O = __importStar(require("../../../Option"))
const AtomicReference_1 = require("../../../Support/AtomicReference")
const C = __importStar(require("../Channel"))
/**
 * Sink is a data type that represent a channel that reads elements
 * of type `In`, handles input errors of type `InErr`, emits errors
 * of type `OutErr`, emits outputs of type `L` and ends with a value
 * of type `Z`.
 */
class Sink {
  constructor(channel) {
    this.channel = channel
  }
}
exports.Sink = Sink
function collectLoop(state) {
  return C.readWithCause(
    (i) => collectLoop(Chunk.concat_(state, i)),
    C.halt,
    (_) => C.end(state)
  )
}
/**
 * A sink that collects all of its inputs into a chunk.
 */
function collectAll() {
  return new Sink(collectLoop(Chunk.empty()))
}
exports.collectAll = collectAll
/**
 * A sink that ignores all of its inputs.
 */
function drain() {
  const drain = C.readWithCause(
    (_) => drain,
    C.halt,
    (_) => C.unit
  )
  return new Sink(drain)
}
exports.drain = drain
/**
 * A sink that executes the provided effectful function for every element fed to it
 * until `f` evaluates to `false`.
 */
function forEachWhile(f) {
  const go = (chunk, idx, len, cont) => {
    if (idx === len) {
      return cont
    } else {
      return C.catchAll_(
        C.chain_(C.fromEffect(f(Chunk.unsafeGet_(chunk, idx))), (b) => {
          if (b) {
            return go(chunk, idx + 1, len, cont)
          } else {
            return C.write(Chunk.drop_(chunk, idx))
          }
        }),
        (e) => C.zipRight_(C.write(Chunk.drop_(chunk, idx)), C.fail(e))
      )
    }
  }
  const process = C.readWithCause(
    (_in) => go(_in, 0, Chunk.size(_in), process),
    (halt) => C.halt(halt),
    (_) => C.end(undefined)
  )
  return new Sink(process)
}
exports.forEachWhile = forEachWhile
/**
 * A sink that executes the provided effectful function for every element fed to it.
 */
function forEach(f) {
  return forEachWhile((_) => T.as_(f(_), true))
}
exports.forEach = forEach
/**
 * A sink that folds its inputs with the provided function, termination predicate and initial state.
 */
function reduce(z) {
  return (cont) => (f) => {
    const reduceChunkSplit = (z, chunk) => (cont) => (f) => {
      const reduce = (s, chunk, idx, len) => {
        if (idx === len) {
          return Tp.tuple(s, Chunk.empty())
        } else {
          const s1 = f(s, Chunk.unsafeGet_(chunk, idx))
          if (cont(s1)) {
            return reduce(s1, chunk, idx + 1, len)
          } else {
            return Tp.tuple(s1, Chunk.drop_(chunk, idx + 1))
          }
        }
      }
      return reduce(z, chunk, 0, Chunk.size(chunk))
    }
    const reader = (s) => {
      if (!cont(s)) {
        return C.end(s)
      } else {
        return C.readWith(
          (_in) => {
            const {
              tuple: [nextS, leftovers]
            } = reduceChunkSplit(s, _in)(cont)(f)
            if (!Chunk.isEmpty(leftovers)) {
              return C.as_(C.write(leftovers), nextS)
            } else {
              return reader(nextS)
            }
          },
          (err) => C.fail(err),
          (_) => C.end(s)
        )
      }
    }
    return new Sink(reader(z))
  }
}
exports.reduce = reduce
/**
 * A sink that effectfully folds its inputs with the provided function, termination predicate and initial state.
 */
function reduceM(z) {
  return (cont) => (f) => {
    const reduceChunkSplit = (z, chunk) => (cont) => (f) => {
      const reduce = (s, chunk, idx, len) => {
        if (idx === len) {
          return T.succeed(Tp.tuple(s, O.none))
        } else {
          return T.chain_(f(s, Chunk.unsafeGet_(chunk, idx)), (s1) => {
            if (cont(s1)) {
              return reduce(s1, chunk, idx + 1, len)
            } else {
              return T.succeed(Tp.tuple(s1, O.some(Chunk.drop_(chunk, idx + 1))))
            }
          })
        }
      }
      return reduce(z, chunk, 0, Chunk.size(chunk))
    }
    const reader = (s) => {
      if (!cont(s)) {
        return C.end(s)
      } else {
        return C.readWith(
          (_in) => {
            return C.chain_(
              C.fromEffect(reduceChunkSplit(s, _in)(cont)(f)),
              ({ tuple: [nextS, leftovers] }) => {
                return O.fold_(
                  leftovers,
                  () => reader(nextS),
                  (l) => C.as_(C.write(l), nextS)
                )
              }
            )
          },
          (err) => C.fail(err),
          (_) => C.end(s)
        )
      }
    }
    return new Sink(reader(z))
  }
}
exports.reduceM = reduceM
/**
 * A sink that executes the provided effectful function for every chunk fed to it.
 */
function forEachChunk(f) {
  return forEachChunkWhile((_) => T.as_(f(_), true))
}
exports.forEachChunk = forEachChunk
/**
 * A sink that executes the provided effectful function for every chunk fed to it
 * until `f` evaluates to `false`.
 */
function forEachChunkWhile(f) {
  const reader = C.readWith(
    (_in) =>
      C.chain_(C.fromEffect(f(_in)), (continue_) =>
        continue_ ? reader : C.end(undefined)
      ),
    (err) => C.fail(err),
    (_) => C.unit
  )
  return new Sink(reader)
}
exports.forEachChunkWhile = forEachChunkWhile
/**
 * Runs this sink until it yields a result, then uses that result to create another
 * sink from the provided function which will continue to run until it yields a result.
 *
 * This function essentially runs sinks in sequence.
 */
function chain_(self, f) {
  return foldM_(self, (_) => fail(_), f)
}
exports.chain_ = chain_
/**
 * Runs this sink until it yields a result, then uses that result to create another
 * sink from the provided function which will continue to run until it yields a result.
 *
 * This function essentially runs sinks in sequence.
 *
 * @dataFirst chain_
 */
function chain(f) {
  return (self) => chain_(self, f)
}
exports.chain = chain
function foldM_(self, failure, success) {
  return new Sink(
    C.foldM_(
      C.doneCollect(self.channel),
      (_) => failure(_).channel,
      ({ tuple: [leftovers, z] }) =>
        C.suspend(() => {
          const leftoversRef = new AtomicReference_1.AtomicReference(
            Chunk.filter_(leftovers, (a) => !Chunk.isEmpty(a))
          )
          const refReader = C.chain_(
            C.succeedWith(() => leftoversRef.getAndSet(Chunk.empty())),
            (chunk) => C.writeChunk(chunk)
          )
          const passthrough = C.identity()
          const continationSink = C.zipRight_(refReader, passthrough)[">>>"](
            success(z).channel
          )
          return C.chain_(
            C.doneCollect(continationSink),
            ({ tuple: [newLeftovers, z1] }) =>
              C.zipRight_(
                C.chain_(
                  C.succeedWith(() => leftoversRef.get),
                  (_) => C.writeChunk(_)
                ),
                C.as_(C.writeChunk(newLeftovers), z1)
              )
          )
        })
    )
  )
}
exports.foldM_ = foldM_
/**
 *
 * @dataFirst foldM_
 */
function foldM(failure, success) {
  return (self) => foldM_(self, failure, success)
}
exports.foldM = foldM
/**
 * A sink that always fails with the specified error.
 */
function fail(e) {
  return new Sink(C.fail(e))
}
exports.fail = fail
//# sourceMappingURL=index.js.map
