import { Adt, Arr, Fun, Obj } from '@ephox/katamari';
const _placeholder = 'placeholder';
const adt = Adt.generate([
    { single: ['required', 'valueThunk'] },
    { multiple: ['required', 'valueThunks'] }
]);
const isSubstituted = (spec) => Obj.has(spec, 'uiType');
const subPlaceholder = (owner, detail, compSpec, placeholders) => {
    if (owner.exists((o) => o !== compSpec.owner)) {
        return adt.single(true, Fun.constant(compSpec));
    }
    // Ignore having to find something for the time being.
    return Obj.get(placeholders, compSpec.name).fold(() => {
        throw new Error('Unknown placeholder component: ' + compSpec.name + '\nKnown: [' +
            Obj.keys(placeholders) + ']\nNamespace: ' + owner.getOr('none') + '\nSpec: ' + JSON.stringify(compSpec, null, 2));
    }, (newSpec) => 
    // Must return a single/multiple type
    newSpec.replace());
};
const scan = (owner, detail, compSpec, placeholders) => {
    if (isSubstituted(compSpec) && compSpec.uiType === _placeholder) {
        return subPlaceholder(owner, detail, compSpec, placeholders);
    }
    else {
        return adt.single(false, Fun.constant(compSpec));
    }
};
const substitute = (owner, detail, compSpec, placeholders) => {
    const base = scan(owner, detail, compSpec, placeholders);
    return base.fold((req, valueThunk) => {
        const value = isSubstituted(compSpec) ? valueThunk(detail, compSpec.config, compSpec.validated) : valueThunk(detail);
        const childSpecs = Obj.get(value, 'components').getOr([]);
        const substituted = Arr.bind(childSpecs, (c) => substitute(owner, detail, c, placeholders));
        return [
            {
                ...value,
                components: substituted
            }
        ];
    }, (req, valuesThunk) => {
        if (isSubstituted(compSpec)) {
            const values = valuesThunk(detail, compSpec.config, compSpec.validated);
            // Allow a preprocessing step for groups before returning the components
            const preprocessor = compSpec.validated.preprocess.getOr(Fun.identity);
            return preprocessor(values);
        }
        else {
            return valuesThunk(detail);
        }
    });
};
const substituteAll = (owner, detail, components, placeholders) => Arr.bind(components, (c) => substitute(owner, detail, c, placeholders));
const oneReplace = (label, replacements) => {
    let called = false;
    const used = () => called;
    const replace = () => {
        if (called) {
            throw new Error('Trying to use the same placeholder more than once: ' + label);
        }
        called = true;
        return replacements;
    };
    const required = () => replacements.fold((req, _) => req, (req, _) => req);
    return {
        name: Fun.constant(label),
        required,
        used,
        replace
    };
};
const substitutePlaces = (owner, detail, components, placeholders) => {
    const ps = Obj.map(placeholders, (ph, name) => oneReplace(name, ph));
    const outcome = substituteAll(owner, detail, components, ps);
    Obj.each(ps, (p) => {
        if (p.used() === false && p.required()) {
            throw new Error('Placeholder: ' + p.name() + ' was not found in components list\nNamespace: ' + owner.getOr('none') + '\nComponents: ' +
                JSON.stringify(detail.components, null, 2));
        }
    });
    return outcome;
};
const singleReplace = (detail, p) => p.fold((req, valueThunk) => [valueThunk(detail)], (req, valuesThunk) => valuesThunk(detail));
const single = adt.single;
const multiple = adt.multiple;
const placeholder = Fun.constant(_placeholder);
export { single, multiple, placeholder, substituteAll, substitutePlaces, singleReplace };
//# sourceMappingURL=UiSubstitutes.js.map