import { cloneElement } from 'react';
import { isBoolean, isString, isNumber, } from '../../services/predicate';
import ReactIs from 'react-is';
function isPrimitive(value) {
    return isBoolean(value) || isString(value) || isNumber(value);
}
function hasPropName(child) {
    return typeof child === 'object' && child.hasOwnProperty('propName');
}
/**
 * Replaces placeholder values in `input` with their matching value in `values`
 * e.g. input:'Hello, {name}' will replace `{name}` with `values[name]`
 * @param {string} input
 * @param {RenderableValues} values
 * @param {Function} i18nMappingFunc
 * @returns {string | React.ReactChild[]}
 */
export function processStringToChildren(input, values, i18nMappingFunc) {
    const children = [];
    let child;
    function appendCharToChild(char) {
        if (child === undefined) {
            // starting a new string literal
            child = char;
        }
        else if (typeof child === 'string') {
            // existing string literal
            child = child + char;
        }
        else if (hasPropName(child)) {
            // adding to the propName of a values lookup
            child.propName = child.propName + char;
        }
    }
    function appendValueToChildren(value) {
        if (value === undefined) {
            return;
        }
        else if (ReactIs.isElement(value)) {
            // an array with any ReactElements will be kept as an array
            // so they need to be assigned a key
            // @ts-ignore
            children.push(cloneElement(value, { key: children.length }));
        }
        else if (hasPropName(value)) {
            // this won't be called, propName children are converted to a ReactChild before calling this
        }
        else {
            // everything else can go straight in
            if (i18nMappingFunc !== undefined && typeof value === 'string') {
                value = i18nMappingFunc(value);
            }
            children.push(value);
        }
    }
    // if we don't encounter a non-primitive
    // then `children` can be concatenated together at the end
    let encounteredNonPrimitive = false;
    // tslint:disable-next-line:prefer-for-of
    for (let i = 0; i < input.length; i++) {
        const char = input[i];
        if (char === '\\') {
            // peek at the next character to know if this is an escape
            const nextChar = input[i + 1];
            let charToAdd = char; // if this isn't an escape sequence then we will add the backslash
            if (nextChar === '{' || nextChar === '}') {
                // escaping a brace
                i += 1; // advance passed the brace
                charToAdd = input[i];
            }
            appendCharToChild(charToAdd);
        }
        else if (char === '{') {
            appendValueToChildren(child);
            child = { propName: '' };
        }
        else if (char === '}') {
            const propName = child.propName;
            if (!values.hasOwnProperty(propName)) {
                throw new Error(`Key "${propName}" not found in ${JSON.stringify(values, null, 2)}`);
            }
            const propValue = values[propName];
            encounteredNonPrimitive =
                encounteredNonPrimitive || !isPrimitive(propValue);
            appendValueToChildren(propValue);
            child = undefined;
        }
        else {
            appendCharToChild(char);
        }
    }
    // include any remaining child value
    appendValueToChildren(child);
    return encounteredNonPrimitive ? children : children.join('');
}
//# sourceMappingURL=SoftI18nUtil.js.map