"use strict";
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatMethod = void 0;
const recreateQueryOnelinerFromSyntaxTree_1 = __importDefault(require("../recreateQueryOnelinerFromSyntaxTree"));
const types_1 = require("../types");
const utils_1 = require("../utils");
const layoutUtils_1 = require("../layoutUtils");
const utils_2 = require("./utils");
// Groups arguments into argument groups an adds a localIndentation property
const formatMethod = (formatSyntaxTree) => (config) => (syntaxTree) => {
    const method = formatSyntaxTree((0, utils_2.withNoEndDotInfo)(config))(syntaxTree.method);
    const argumentsWillNotBeWrapped = (0, layoutUtils_1.willFitOnLine)(syntaxTree, config, config.horizontalPosition);
    if (argumentsWillNotBeWrapped) {
        const recreatedQuery = (0, recreateQueryOnelinerFromSyntaxTree_1.default)(config, config.horizontalPosition)(syntaxTree);
        return {
            type: types_1.TokenType.Method,
            method,
            // The arguments property is here so that the resulted syntax tree can
            // still be understood by recreateQueryOnelinerFromSyntaxTree
            arguments: syntaxTree.arguments,
            argumentGroups: [
                syntaxTree.arguments.reduce((argumentGroup, syntaxTree) => {
                    return [
                        ...argumentGroup,
                        formatSyntaxTree(
                        // Since the method's arguments will be on the same line, their horizontal position is increased by the
                        // method's width plus the width of the opening parenthesis
                        (0, utils_1.pipe)(utils_2.withZeroIndentation, utils_2.withZeroDotInfo, (0, utils_2.withIncreasedHorizontalPosition)(method.width +
                            1 +
                            (config.shouldStartWithDot ? 1 : 0) +
                            argumentGroup.map(({ width }) => width).reduce(utils_1.sum, 0) +
                            argumentGroup.length))(config))(syntaxTree),
                    ];
                }, []),
            ],
            argumentsShouldStartOnNewLine: false,
            shouldStartWithDot: false,
            shouldEndWithDot: Boolean(config.shouldEndWithDot),
            localIndentation: config.localIndentation,
            horizontalPosition: config.horizontalPosition,
            width: recreatedQuery.trim().length,
        };
    }
    // shouldEndWithDot has to reside on the method object, so the end dot can be
    // placed after the method parentheses. shouldStartWithDot has to be passed on
    // further down so the start dot can be placed after the indentation.
    const horizontalPosition = config.horizontalPosition + method.width + (config.shouldStartWithDot ? 1 : 0) + 1;
    const greedyArgumentGroups = syntaxTree.arguments.reduce((acc, arg) => {
        const formattedArg = formatSyntaxTree((0, utils_1.pipe)(utils_2.withZeroIndentation, utils_2.withZeroDotInfo, (0, utils_2.withHorizontalPosition)(horizontalPosition))(config))(arg);
        const currentGroupWidth = acc.currentGroup.map(({ width }) => width).reduce(utils_1.sum, 0) + Math.max(acc.currentGroup.length - 1, 0) * 2;
        const potentialGroupWidth = acc.currentGroup.length > 0 ? currentGroupWidth + 2 + formattedArg.width : formattedArg.width;
        if (acc.currentGroup.length > 0 && horizontalPosition + potentialGroupWidth > config.maxLineLength) {
            return {
                groups: [...acc.groups, acc.currentGroup],
                currentGroup: [
                    formatSyntaxTree((0, utils_1.pipe)((0, utils_2.withIndentation)(horizontalPosition), utils_2.withZeroDotInfo, (0, utils_2.withHorizontalPosition)(horizontalPosition))(config))(arg),
                ],
            };
        }
        return {
            ...acc,
            currentGroup: [
                ...acc.currentGroup,
                formatSyntaxTree((0, utils_1.pipe)((0, utils_2.withIndentation)(horizontalPosition), utils_2.withZeroDotInfo, (0, utils_2.withHorizontalPosition)(horizontalPosition))(config))(arg),
            ],
        };
    }, { groups: [], currentGroup: [] });
    const argumentGroups = greedyArgumentGroups.currentGroup.length > 0
        ? [...greedyArgumentGroups.groups, greedyArgumentGroups.currentGroup]
        : greedyArgumentGroups.groups;
    const indentationWidth = horizontalPosition;
    const greedyFits = argumentGroups.every((group) => {
        const groupWidth = group.map(({ width }) => width).reduce(utils_1.sum, 0) + (group.length - 1) * 2;
        return indentationWidth + groupWidth <= config.maxLineLength;
    });
    if (greedyFits) {
        const lastArgumentGroup = (0, utils_1.last)(argumentGroups);
        // Add the width of the last line of parameters, the dots between them and the indentation of the parameters
        const width = lastArgumentGroup
            ? lastArgumentGroup.map(({ width }) => width).reduce(utils_1.sum, 0) + (lastArgumentGroup.length - 1) * 2
            : 0;
        return {
            type: types_1.TokenType.Method,
            method,
            arguments: syntaxTree.arguments,
            argumentGroups,
            argumentsShouldStartOnNewLine: false,
            localIndentation: config.localIndentation,
            horizontalPosition: config.horizontalPosition,
            shouldStartWithDot: false,
            shouldEndWithDot: Boolean(config.shouldEndWithDot),
            width,
        };
    }
    // Fallback to wrap-all
    const wrapAllArgumentGroups = syntaxTree.arguments.map((step) => [
        formatSyntaxTree((0, utils_1.pipe)((0, utils_2.withIndentation)(horizontalPosition), utils_2.withZeroDotInfo, (0, utils_2.withHorizontalPosition)(horizontalPosition))(config))(step),
    ]);
    const lastWrapAllArgumentGroup = (0, utils_1.last)(wrapAllArgumentGroups);
    const wrapAllWidth = lastWrapAllArgumentGroup
        ? lastWrapAllArgumentGroup.map(({ width }) => width).reduce(utils_1.sum, 0) + (lastWrapAllArgumentGroup.length - 1) * 2
        : 0;
    return {
        type: types_1.TokenType.Method,
        method,
        arguments: syntaxTree.arguments,
        argumentGroups: wrapAllArgumentGroups,
        argumentsShouldStartOnNewLine: false,
        localIndentation: config.localIndentation,
        horizontalPosition: config.horizontalPosition,
        shouldStartWithDot: false,
        shouldEndWithDot: Boolean(config.shouldEndWithDot),
        width: wrapAllWidth,
    };
};
exports.formatMethod = formatMethod;
