import { identity } from "@thi.ng/api/fn";
import { implementsFunction } from "@thi.ng/checks/implements-function";
import { isArrayLike } from "@thi.ng/checks/is-arraylike";
import { isIterable } from "@thi.ng/checks/is-iterable";
import { illegalArity } from "@thi.ng/errors/illegal-arity";
import { isReduced, unreduced } from "./reduced.js";
const __parseArgs = (args) => args.length === 2 ? [void 0, args[1]] : args.length === 3 ? [args[1], args[2]] : illegalArity(args.length);
function reduce(...args) {
  const rfn = args[0];
  const init = rfn[0];
  const complete = rfn[1];
  const reduce2 = rfn[2];
  args = __parseArgs(args);
  const acc = args[0] == null ? init() : args[0];
  const src = args[1];
  return unreduced(
    complete(
      implementsFunction(src, "$reduce") ? src.$reduce(reduce2, acc) : isArrayLike(src) ? __reduceArray(reduce2, acc, src) : __reduceIterable(reduce2, acc, src)
    )
  );
}
function reduceRight(...args) {
  const [init, complete, reduce2] = args[0];
  args = __parseArgs(args);
  let acc = args[0] == null ? init() : args[0];
  const src = args[1];
  for (let i = src.length; i-- > 0; ) {
    acc = reduce2(acc, src[i]);
    if (isReduced(acc)) {
      acc = acc.deref();
      break;
    }
  }
  return unreduced(complete(acc));
}
const __reduceArray = (rfn, acc, src) => {
  for (let i = 0, n = src.length; i < n; i++) {
    acc = rfn(acc, src[i]);
    if (isReduced(acc)) {
      acc = acc.deref();
      break;
    }
  }
  return acc;
};
const __reduceIterable = (rfn, acc, src) => {
  for (let x of src) {
    acc = rfn(acc, x);
    if (isReduced(acc)) {
      acc = acc.deref();
      break;
    }
  }
  return acc;
};
const reducer = (init, rfn) => [init, identity, rfn];
const $$reduce = (rfn, args) => {
  const n = args.length - 1;
  return isIterable(args[n]) ? args.length > 1 ? reduce(rfn.apply(null, args.slice(0, n)), args[n]) : reduce(rfn(), args[0]) : void 0;
};
export {
  $$reduce,
  reduce,
  reduceRight,
  reducer
};
