/******************************************************************************
 * Copyright 2023 TypeFox GmbH
 * This program and the accompanying materials are made available under the
 * terms of the MIT License, which is available in the project root.
 ******************************************************************************/
import { CompositeGeneratorNode, isGeneratorNode, NewLineNode, traceToNode } from './generator-node.js';
const defaultToGenerated = (e) => e === undefined || typeof e === 'string' || isGeneratorNode(e) ? e : String(e);
export function joinToNode(iterable, toGeneratedOrOptions = defaultToGenerated, options = {}) {
    const toGenerated = typeof toGeneratedOrOptions === 'function' ? toGeneratedOrOptions : defaultToGenerated;
    const { filter, prefix, suffix, separator, appendNewLineIfNotEmpty, skipNewLineAfterLastItem } = typeof toGeneratedOrOptions === 'object' ? toGeneratedOrOptions : options;
    const prefixFunc = typeof prefix === 'function' ? prefix : (() => prefix);
    const suffixFunc = typeof suffix === 'function' ? suffix : (() => suffix);
    const doAppendNewLines = (node) => {
        if (node.isEmpty()) {
            // do nothing
            return;
        }
        else if (appendNewLineIfNotEmpty === true || appendNewLineIfNotEmpty === 1) {
            node.appendNewLineIfNotEmpty();
        }
        else {
            node.append(new NewLineNode(undefined, true, appendNewLineIfNotEmpty));
        }
    };
    return reduceWithIsLast(iterable, (node, it, i, isLast) => {
        if (filter && !filter(it, i, isLast)) {
            return node;
        }
        const content = toGenerated(it, i, isLast);
        return content === undefined ? /* in this case don't append anything to */ node : /* otherwise: */ (node ?? (node = new CompositeGeneratorNode()))
            .appendIf(!node.isEmpty(), separator)
            .appendIf(
        // append 'newLineIfNotEmpty' elements only if 'node' has some content already,
        //  as if the parent is an IndentNode with 'indentImmediately' set to 'false'
        //  the indentation is not properly applied to the first non-empty line of the (this) child node
        appendNewLineIfNotEmpty !== undefined, doAppendNewLines)
            .append(prefixFunc(it, i, isLast))
            .append(content)
            .append(suffixFunc(it, i, isLast));
    })?.appendIf(!skipNewLineAfterLastItem && appendNewLineIfNotEmpty !== undefined, doAppendNewLines);
}
// implementation:
export function joinTracedToNode(source, property) {
    return (iterable, toGeneratedOrOptions, options) => {
        options ?? (options = typeof toGeneratedOrOptions === 'object' ? toGeneratedOrOptions : undefined);
        const toGenerated = typeof toGeneratedOrOptions === 'function' ? toGeneratedOrOptions : defaultToGenerated;
        return traceToNode(source, property)(joinToNode(iterable, source && property ? (element, index, isLast) => traceToNode(source, property, index)(toGenerated(element, index, isLast)) : toGenerated, options));
    };
}
// implementation:
export function joinTracedToNodeIf(condition, source, property) {
    return condition ? joinTracedToNode((typeof source === 'function' ? source() : source), property) : () => undefined;
}
function reduceWithIsLast(iterable, callbackfn, initial) {
    const iterator = iterable[Symbol.iterator]();
    let next = iterator.next();
    let index = 0;
    let result = initial;
    while (!next.done) {
        const nextNext = iterator.next();
        result = callbackfn(result, next.value, index, Boolean(nextNext.done));
        next = nextNext;
        index++;
    }
    return result;
}
//# sourceMappingURL=node-joiner.js.map