"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function appendPropertyInAstObject(recorder, node, propertyName, value, indent) {
    const indentStr = _buildIndent(indent);
    let index = node.start.offset + 1;
    if (node.properties.length > 0) {
        // Insert comma.
        const last = node.properties[node.properties.length - 1];
        const { text, end } = last;
        const commaIndex = text.endsWith('\n') ? end.offset - 1 : end.offset;
        recorder.insertRight(commaIndex, ',');
        index = end.offset;
    }
    const content = _stringifyContent(value, indentStr);
    recorder.insertRight(index, (node.properties.length === 0 && indent ? '\n' : '')
        + ' '.repeat(indent)
        + `"${propertyName}":${indent ? ' ' : ''}${content}`
        + indentStr.slice(0, -indent));
}
exports.appendPropertyInAstObject = appendPropertyInAstObject;
function insertPropertyInAstObjectInOrder(recorder, node, propertyName, value, indent) {
    if (node.properties.length === 0) {
        appendPropertyInAstObject(recorder, node, propertyName, value, indent);
        return;
    }
    // Find insertion info.
    let insertAfterProp = null;
    let prev = null;
    let isLastProp = false;
    const last = node.properties[node.properties.length - 1];
    for (const prop of node.properties) {
        if (prop.key.value > propertyName) {
            if (prev) {
                insertAfterProp = prev;
            }
            break;
        }
        if (prop === last) {
            isLastProp = true;
            insertAfterProp = last;
        }
        prev = prop;
    }
    if (isLastProp) {
        appendPropertyInAstObject(recorder, node, propertyName, value, indent);
        return;
    }
    const indentStr = _buildIndent(indent);
    const insertIndex = insertAfterProp === null
        ? node.start.offset + 1
        : insertAfterProp.end.offset + 1;
    const content = _stringifyContent(value, indentStr);
    recorder.insertRight(insertIndex, indentStr
        + `"${propertyName}":${indent ? ' ' : ''}${content}`
        + ',');
}
exports.insertPropertyInAstObjectInOrder = insertPropertyInAstObjectInOrder;
function removePropertyInAstObject(recorder, node, propertyName) {
    // Find the property inside the object.
    const propIdx = node.properties.findIndex(prop => prop.key.value === propertyName);
    if (propIdx === -1) {
        // There's nothing to remove.
        return;
    }
    if (node.properties.length === 1) {
        // This is a special case. Everything should be removed, including indentation.
        recorder.remove(node.start.offset, node.end.offset - node.start.offset);
        recorder.insertRight(node.start.offset, '{}');
        return;
    }
    // The AST considers commas and indentation to be part of the preceding property.
    // To get around messy comma and identation management, we can work over the range between
    // two properties instead.
    const previousProp = node.properties[propIdx - 1];
    const targetProp = node.properties[propIdx];
    const nextProp = node.properties[propIdx + 1];
    let start, end;
    if (previousProp) {
        // Given the object below, and intending to remove the `m` property:
        // "{\n  \"a\": \"a\",\n  \"m\": \"m\",\n  \"z\": \"z\"\n}"
        //                        ^---------------^
        // Removing the range above results in:
        // "{\n  \"a\": \"a\",\n  \"z\": \"z\"\n}"
        start = previousProp.end;
        end = targetProp.end;
    }
    else {
        // If there's no previousProp there is a nextProp, since we've specialcased the 1 length case.
        // Given the object below, and intending to remove the `a` property:
        // "{\n  \"a\": \"a\",\n  \"m\": \"m\",\n  \"z\": \"z\"\n}"
        //       ^---------------^
        // Removing the range above results in:
        // "{\n  \"m\": \"m\",\n  \"z\": \"z\"\n}"
        start = targetProp.start;
        end = nextProp.start;
    }
    recorder.remove(start.offset, end.offset - start.offset);
    if (!nextProp) {
        recorder.insertRight(start.offset, '\n');
    }
}
exports.removePropertyInAstObject = removePropertyInAstObject;
function appendValueInAstArray(recorder, node, value, indent = 4) {
    let indentStr = _buildIndent(indent);
    let index = node.start.offset + 1;
    // tslint:disable-next-line: no-any
    let newNodes;
    if (node.elements.length > 0) {
        // Insert comma.
        const { end } = node.elements[node.elements.length - 1];
        const isClosingOnSameLine = node.end.offset - end.offset === 1;
        if (isClosingOnSameLine && indent) {
            // Reformat the entire array
            recorder.remove(node.start.offset, node.end.offset - node.start.offset);
            newNodes = [
                ...node.elements.map(({ value }) => value),
                value,
            ];
            index = node.start.offset;
            // In case we are generating the entire node we need to reduce the spacing as
            // otherwise we'd end up having incorrect double spacing
            indent = indent - 2;
            indentStr = _buildIndent(indent);
        }
        else {
            recorder.insertRight(end.offset, ',');
            index = end.offset;
        }
    }
    recorder.insertRight(index, (newNodes ? '' : indentStr)
        + _stringifyContent(newNodes || value, indentStr)
        + (node.elements.length === 0 && indent ? indentStr.substr(0, -indent) + '\n' : ''));
}
exports.appendValueInAstArray = appendValueInAstArray;
function findPropertyInAstObject(node, propertyName) {
    let maybeNode = null;
    for (const property of node.properties) {
        if (property.key.value == propertyName) {
            maybeNode = property.value;
        }
    }
    return maybeNode;
}
exports.findPropertyInAstObject = findPropertyInAstObject;
function _buildIndent(count) {
    return count ? '\n' + ' '.repeat(count) : '';
}
function _stringifyContent(value, indentStr) {
    // TODO: Add snapshot tests
    // The 'space' value is 2, because we want to add 2 additional
    // indents from the 'key' node.
    // If we use the indent provided we will have double indents:
    // "budgets": [
    //   {
    //     "type": "initial",
    //     "maximumWarning": "2mb",
    //     "maximumError": "5mb"
    //   },
    //   {
    //       "type": "anyComponentStyle",
    //       'maximumWarning": "5kb"
    //   }
    // ]
    return JSON.stringify(value, null, 2).replace(/\n/g, indentStr);
}