"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.transformTemplateBindings = void 0;
const ng = require("@angular/compiler/src/expression_parser/ast");
const transform_1 = require("./transform");
const utils_1 = require("./utils");
function transformTemplateBindings(rawTemplateBindings, context) {
    rawTemplateBindings.forEach(fixTemplateBindingSpan);
    const [firstTemplateBinding] = rawTemplateBindings;
    const { key: prefix } = firstTemplateBinding;
    const templateBindings = context.text
        .slice(firstTemplateBinding.sourceSpan.start, firstTemplateBinding.sourceSpan.end)
        .trim().length === 0
        ? rawTemplateBindings.slice(1)
        : rawTemplateBindings;
    const body = [];
    let lastTemplateBinding = null;
    for (let i = 0; i < templateBindings.length; i++) {
        const templateBinding = templateBindings[i];
        if (lastTemplateBinding &&
            isExpressionBinding(lastTemplateBinding) &&
            isVariableBinding(templateBinding) &&
            templateBinding.value &&
            templateBinding.value.source === lastTemplateBinding.key.source) {
            const alias = _c('NGMicrosyntaxKey', { name: templateBinding.key.source }, templateBinding.key.span);
            const updateSpanEnd = (node, end) => (Object.assign(Object.assign({}, node), transform_1.transformSpan({ start: node.start, end }, context)));
            const updateExpressionAlias = (expression) => (Object.assign(Object.assign({}, updateSpanEnd(expression, alias.end)), { alias }));
            const lastNode = body.pop();
            // istanbul ignore else
            if (lastNode.type === 'NGMicrosyntaxExpression') {
                body.push(updateExpressionAlias(lastNode));
            }
            else if (lastNode.type === 'NGMicrosyntaxKeyedExpression') {
                const expression = updateExpressionAlias(lastNode.expression);
                body.push(updateSpanEnd(Object.assign(Object.assign({}, lastNode), { expression }), expression.end));
            }
            else {
                throw new Error(`Unexpected type ${lastNode.type}`);
            }
        }
        else {
            body.push(transformTemplateBinding(templateBinding, i));
        }
        lastTemplateBinding = templateBinding;
    }
    return _c('NGMicrosyntax', { body }, body.length === 0
        ? rawTemplateBindings[0].sourceSpan
        : { start: body[0].start, end: body[body.length - 1].end });
    function transformTemplateBinding(templateBinding, index) {
        if (isExpressionBinding(templateBinding)) {
            const { key, value } = templateBinding;
            if (!value) {
                return _c('NGMicrosyntaxKey', { name: removePrefix(key.source) }, key.span);
            }
            else if (index === 0) {
                return _c('NGMicrosyntaxExpression', { expression: _t(value.ast), alias: null }, value.sourceSpan);
            }
            else {
                return _c('NGMicrosyntaxKeyedExpression', {
                    key: _c('NGMicrosyntaxKey', { name: removePrefix(key.source) }, key.span),
                    expression: _c('NGMicrosyntaxExpression', { expression: _t(value.ast), alias: null }, value.sourceSpan),
                }, { start: key.span.start, end: value.sourceSpan.end });
            }
        }
        else {
            const { key, sourceSpan } = templateBinding;
            const startsWithLet = /^let\s$/.test(context.text.slice(sourceSpan.start, sourceSpan.start + 4));
            if (startsWithLet) {
                const { value } = templateBinding;
                return _c('NGMicrosyntaxLet', {
                    key: _c('NGMicrosyntaxKey', { name: key.source }, key.span),
                    value: !value
                        ? null
                        : _c('NGMicrosyntaxKey', { name: value.source }, value.span),
                }, {
                    start: sourceSpan.start,
                    end: value ? value.span.end : key.span.end,
                });
            }
            else {
                const value = getAsVariableBindingValue(templateBinding);
                return _c('NGMicrosyntaxAs', {
                    key: _c('NGMicrosyntaxKey', { name: value.source }, value.span),
                    alias: _c('NGMicrosyntaxKey', { name: key.source }, key.span),
                }, { start: value.span.start, end: key.span.end });
            }
        }
    }
    function _t(n) {
        return transform_1.transform(n, context);
    }
    function _c(t, n, span, stripSpaces = true) {
        return Object.assign(Object.assign({ type: t }, transform_1.transformSpan(span, context, stripSpaces)), n);
    }
    function removePrefix(string) {
        return utils_1.toLowerCamelCase(string.slice(prefix.source.length));
    }
    function isExpressionBinding(templateBinding) {
        return templateBinding instanceof ng.ExpressionBinding;
    }
    function isVariableBinding(templateBinding) {
        return templateBinding instanceof ng.VariableBinding;
    }
    function fixTemplateBindingSpan(templateBinding) {
        fixSpan(templateBinding.key.span);
        if (isVariableBinding(templateBinding) && templateBinding.value) {
            fixSpan(templateBinding.value.span);
        }
    }
    /**
     * - "a"  (start=0 end=1) -> (start=0 end=3)
     * - '\'' (start=0 end=1) -> (start=0 end=4)
     */
    function fixSpan(span) {
        if (context.text[span.start] !== '"' && context.text[span.start] !== "'") {
            return;
        }
        const quote = context.text[span.start];
        let hasBackSlash = false;
        for (let i = span.start + 1; i < context.text.length; i++) {
            switch (context.text[i]) {
                case quote:
                    if (!hasBackSlash) {
                        span.end = i + 1;
                        return;
                    }
                // fall through
                default:
                    hasBackSlash = false;
                    break;
                case '\\':
                    hasBackSlash = !hasBackSlash;
                    break;
            }
        }
    }
    /**
     * - "as b" (value="NgEstreeParser" key="b") -> (value="$implicit" key="b")
     */
    function getAsVariableBindingValue(variableBinding) {
        if (!variableBinding.value ||
            variableBinding.value.source !== utils_1.NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX) {
            return variableBinding.value;
        }
        const index = utils_1.findBackChar(/\S/, variableBinding.sourceSpan.start, context.text);
        return {
            source: '$implicit',
            span: { start: index, end: index },
        };
    }
}
exports.transformTemplateBindings = transformTemplateBindings;
