export const LlamaText = function LlamaText(strings, ...values) {
    return createLlamaText(createHistoryFromStringsAndValues(strings, values));
};
LlamaText.fromJSON = function fromJSON(json) {
    return createLlamaText(json.map((value) => {
        if (typeof value === "string")
            return value;
        else if (SpecialToken.isSpecialTokenJSON(value))
            return SpecialToken.fromJSON(value);
        else {
            void (value);
            throw new Error(`Unknown value type: ${value}`);
        }
    }));
};
export class SpecialToken {
    value;
    constructor(value) {
        this.value = value;
    }
    toString() {
        return this.value;
    }
    tokenize(tokenizer) {
        return tokenizer(this.value, true);
    }
    toJSON() {
        return {
            type: "specialToken",
            value: this.value
        };
    }
    static fromJSON(json) {
        if (json.builtin)
            return new BuiltinSpecialToken(json.value);
        else
            return new SpecialToken(json.value);
    }
    static isSpecialTokenJSON(value) {
        return value != null && typeof value === "object" && value.type === "specialToken";
    }
}
export class BuiltinSpecialToken extends SpecialToken {
    value;
    constructor(value) {
        super(value);
        this.value = value;
    }
    tokenize(tokenizer) {
        return tokenizer(this.value, "builtin");
    }
    toJSON() {
        return {
            type: "specialToken",
            value: this.value,
            builtin: true
        };
    }
}
export function isLlamaText(value) {
    return typeof value === "function" && "type" in value && value.type === "LlamaText";
}
export function tokenizeText(text, tokenizer) {
    if (typeof text === "string")
        return tokenizer(text, false);
    else
        return text.tokenize(tokenizer);
}
const LlamaTextPrototypeFunctions = {
    mapValues: function mapValues(mapper) {
        return createLlamaText(this.values.map(mapper));
    },
    joinValues(separator) {
        const newValues = [];
        for (let i = 0; i < this.values.length; i++) {
            newValues.push(this.values[i]);
            if (i !== this.values.length - 1) {
                if (isLlamaText(separator))
                    newValues.push(...separator.values);
                else
                    newValues.push(separator);
            }
        }
        return createLlamaText(newValues);
    },
    toString() {
        return this.values
            .map((value) => {
            if (value instanceof SpecialToken)
                return value.toString();
            else
                return value;
        })
            .join("");
    },
    tokenize(tokenizer) {
        let textToTokenize = "";
        const res = [];
        for (const value of this.values) {
            if (value instanceof SpecialToken) {
                res.push(...tokenizer(textToTokenize, false), ...value.tokenize(tokenizer));
                textToTokenize = "";
            }
            else
                textToTokenize += value;
        }
        res.push(...tokenizer(textToTokenize, false));
        return res;
    },
    toJSON() {
        return this.values.map((value) => {
            if (value instanceof SpecialToken)
                return { type: "specialToken", value: value.value };
            else
                return value;
        });
    }
};
function createLlamaText(history) {
    const llamaText = function LlamaText(strings, ...values) {
        return createLlamaText(llamaText.values.concat(createHistoryFromStringsAndValues(strings, values)));
    };
    Object.defineProperties(llamaText, {
        ["type"]: {
            value: "LlamaText",
            writable: false,
            configurable: false,
            enumerable: true
        },
        ["values"]: {
            value: Object.freeze(history.slice()),
            writable: false,
            configurable: false,
            enumerable: true
        },
        ["mapValues"]: {
            value: LlamaTextPrototypeFunctions.mapValues,
            writable: false,
            configurable: false,
            enumerable: false
        },
        ["joinValues"]: {
            value: LlamaTextPrototypeFunctions.joinValues,
            writable: false,
            configurable: false,
            enumerable: false
        },
        ["toString"]: {
            value: LlamaTextPrototypeFunctions.toString,
            writable: false,
            configurable: false,
            enumerable: false
        },
        ["tokenize"]: {
            value: LlamaTextPrototypeFunctions.tokenize,
            writable: false,
            configurable: false,
            enumerable: false
        },
        ["toJSON"]: {
            value: LlamaTextPrototypeFunctions.toJSON,
            writable: false,
            configurable: false,
            enumerable: false
        }
    });
    return llamaText;
}
function createHistoryFromStringsAndValues(strings, values) {
    function addItemToRes(res, item) {
        if (item === undefined || item === "")
            return res;
        else if (typeof item === "string" || item instanceof SpecialToken)
            return res.concat([item]);
        else if (isLlamaText(item))
            return res.concat(item.values);
        else if (item instanceof Array) {
            return res.concat(item.reduce((res, value) => {
                if (isLlamaText(value))
                    return res.concat(value.values);
                else if (value === "")
                    return res;
                return res.concat([value]);
            }, []));
        }
        else if (typeof item === "number" || typeof item === "boolean")
            return res.concat([String(item)]);
        return item;
    }
    if (!isTemplateStringsArray(strings)) {
        return [strings]
            .concat(values)
            .reduce(addItemToRes, []);
    }
    let res = [];
    for (let i = 0; i < strings.length; i++) {
        res.push(strings[i]);
        if (i < values.length)
            res = addItemToRes(res, values[i]);
    }
    return res;
}
function isTemplateStringsArray(value) {
    return value instanceof Array && value.raw instanceof Array &&
        value.length === value.raw.length;
}
//# sourceMappingURL=LlamaText.js.map