"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 __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
    if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
        if (ar || !(i in from)) {
            if (!ar) ar = Array.prototype.slice.call(from, 0, i);
            ar[i] = from[i];
        }
    }
    return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseToSyntaxTrees = exports.parseNonGremlinCodeToSyntaxTree = void 0;
var types_1 = require("../types");
var utils_1 = require("../utils");
var extractGremlinQueries_1 = require("./extractGremlinQueries");
var tokenizeOnTopLevelPunctuation = function (query) {
    var word = '';
    var parenthesesCount = 0;
    var squareBracketCount = 0;
    var curlyBracketCount = 0;
    var isInsideSingleQuoteString = false;
    query.split('').forEach(function (char) {
        if (char === '(' && !isInsideSingleQuoteString) {
            parenthesesCount++;
            word += '(';
            return;
        }
        if (char === '[' && !isInsideSingleQuoteString) {
            squareBracketCount++;
            word += '[';
            return;
        }
        if (char === '{' && !isInsideSingleQuoteString) {
            curlyBracketCount++;
            word += '{';
            return;
        }
        if (char === ')' && !isInsideSingleQuoteString) {
            parenthesesCount--;
            word += ')';
            return;
        }
        if (char === ']' && !isInsideSingleQuoteString) {
            squareBracketCount--;
            word += ']';
            return;
        }
        if (char === '}' && !isInsideSingleQuoteString) {
            curlyBracketCount--;
            word += '}';
            return;
        }
        if (char === "'") {
            isInsideSingleQuoteString = !isInsideSingleQuoteString;
            word += "'";
            return;
        }
        if (char === '.') {
            word +=
                isInsideSingleQuoteString || parenthesesCount || squareBracketCount || curlyBracketCount
                    ? '.'
                    : String.fromCharCode(28);
            return;
        }
        word += char;
    });
    return word
        .split(String.fromCharCode(28))
        .filter(function (token) { return token !== ''; })
        .map(function (token) { return token.trim(); });
};
var tokenizeOnTopLevelComma = function (query) {
    var word = '';
    var parenthesesCount = 0;
    var squareBracketsCount = 0;
    var curlyBracketsCount = 0;
    var isInsideSingleQuoteString = false;
    query.split('').forEach(function (char) {
        if (char === '(' && !isInsideSingleQuoteString) {
            parenthesesCount++;
            word += '(';
            return;
        }
        if (char === '[' && !isInsideSingleQuoteString) {
            squareBracketsCount++;
            word += '[';
            return;
        }
        if (char === '{' && !isInsideSingleQuoteString) {
            curlyBracketsCount++;
            word += '{';
            return;
        }
        if (char === ')' && !isInsideSingleQuoteString) {
            parenthesesCount--;
            word += ')';
            return;
        }
        if (char === ']' && !isInsideSingleQuoteString) {
            squareBracketsCount--;
            word += ']';
            return;
        }
        if (char === '}' && !isInsideSingleQuoteString) {
            curlyBracketsCount--;
            word += '}';
            return;
        }
        if (char === "'") {
            isInsideSingleQuoteString = !isInsideSingleQuoteString;
            word += "'";
            return;
        }
        if (char === ',') {
            word +=
                isInsideSingleQuoteString || parenthesesCount || squareBracketsCount || curlyBracketsCount
                    ? ','
                    : String.fromCharCode(28);
            return;
        }
        word += char;
    });
    return word
        .split(String.fromCharCode(28))
        .filter(function (token) { return token !== ''; })
        .map(function (token) { return token.trim(); });
};
var tokenizeOnTopLevelParentheses = function (query) {
    var word = '';
    var parenthesesCount = 0;
    var squareBracketsCount = 0;
    var curlyBracketsCount = 0;
    var isInsideSingleQuoteString = false;
    query.split('').forEach(function (char) {
        if (char === '(' && !isInsideSingleQuoteString) {
            if (parenthesesCount === 0) {
                word += String.fromCharCode(28);
            }
            parenthesesCount++;
            word += '(';
            return;
        }
        if (char === '[' && !isInsideSingleQuoteString) {
            squareBracketsCount++;
            word += '[';
            return;
        }
        if (char === '{' && !isInsideSingleQuoteString) {
            curlyBracketsCount++;
            word += '{';
            return;
        }
        if (char === ')' && !isInsideSingleQuoteString) {
            parenthesesCount--;
            word += ')';
            return;
        }
        if (char === ']' && !isInsideSingleQuoteString) {
            squareBracketsCount--;
            word += ']';
            return;
        }
        if (char === '}' && !isInsideSingleQuoteString) {
            curlyBracketsCount--;
            word += '}';
            return;
        }
        if (char === "'") {
            isInsideSingleQuoteString = !isInsideSingleQuoteString;
            word += "'";
            return;
        }
        word += char;
    });
    return word
        .split(String.fromCharCode(28))
        .filter(function (token) { return token !== ''; })
        .map(function (token) { return token.trim(); });
};
var tokenizeOnTopLevelCurlyBrackets = function (query) {
    var word = '';
    var parenthesesCount = 0;
    var squareBracketsCount = 0;
    var curlyBracketsCount = 0;
    var isInsideSingleQuoteString = false;
    query.split('').forEach(function (char) {
        if (char === '(' && !isInsideSingleQuoteString) {
            parenthesesCount++;
            word += '(';
            return;
        }
        if (char === '[' && !isInsideSingleQuoteString) {
            squareBracketsCount++;
            word += '[';
            return;
        }
        if (char === '{' && !isInsideSingleQuoteString) {
            if (curlyBracketsCount === 0) {
                word += String.fromCharCode(28);
            }
            curlyBracketsCount++;
            word += '{';
            return;
        }
        if (char === ')' && !isInsideSingleQuoteString) {
            parenthesesCount--;
            word += ')';
            return;
        }
        if (char === ']' && !isInsideSingleQuoteString) {
            squareBracketsCount--;
            word += ']';
            return;
        }
        if (char === '}' && !isInsideSingleQuoteString) {
            curlyBracketsCount--;
            word += '}';
            return;
        }
        if (char === "'") {
            isInsideSingleQuoteString = !isInsideSingleQuoteString;
            word += "'";
            return;
        }
        word += char;
    });
    return word
        .split(String.fromCharCode(28))
        .filter(function (token) { return token !== ''; })
        .map(function (token) { return token.trim(); });
};
var isWrappedInParentheses = function (token) {
    if (token.length < 2)
        return false;
    if (token.charAt(0) !== '(')
        return false;
    if (token.slice(-1) !== ')')
        return false;
    return true;
};
var isWrappedInCurlyBrackets = function (token) {
    if (token.length < 2)
        return false;
    if (token.charAt(0) !== '{')
        return false;
    if (token.slice(-1) !== '}')
        return false;
    return true;
};
var isString = function (token) {
    if (token.length < 2)
        return false;
    if (token.charAt(0) !== token.substr(-1))
        return false;
    if (['"', "'"].includes(token.charAt(0)))
        return true;
    return false;
};
var isMethodInvocation = function (token) {
    return (0, utils_1.pipe)(tokenizeOnTopLevelParentheses, utils_1.last, isWrappedInParentheses)(token);
};
var isClosureInvocation = function (token) {
    return (0, utils_1.pipe)(tokenizeOnTopLevelCurlyBrackets, utils_1.last, isWrappedInCurlyBrackets)(token);
};
var trimParentheses = function (expression) { return expression.slice(1, -1); };
var trimCurlyBrackets = function (expression) { return expression.slice(1, -1); };
var getMethodTokenAndArgumentTokensFromMethodInvocation = function (token) {
    // The word before the first parenthesis is the method name
    // The token may be a double application of a curried function, so we cannot
    // assume that the first opening parenthesis is closed by the last closing
    // parenthesis
    var tokens = tokenizeOnTopLevelParentheses(token);
    return {
        methodToken: tokens.slice(0, -1).join(''),
        argumentTokens: (0, utils_1.pipe)(trimParentheses, tokenizeOnTopLevelComma)(tokens.slice(-1)[0]),
    };
};
var getIndentation = function (lineOfCode) { return lineOfCode.split('').findIndex((0, utils_1.neq)(' ')); };
var getMethodTokenAndClosureCodeBlockFromClosureInvocation = function (token, fullQuery) {
    // The word before the first curly bracket is the method name
    // The token may be a double application of a curried function, so we cannot
    // assume that the first opening curly bracket is closed by the last closing
    // curly bracket
    var tokens = tokenizeOnTopLevelCurlyBrackets(token);
    var methodToken = tokens.slice(0, -1).join('');
    var closureCodeBlockToken = trimCurlyBrackets(tokens.slice(-1)[0]);
    var initialClosureCodeBlockIndentation = fullQuery
        .substr(0, fullQuery.indexOf(closureCodeBlockToken))
        .split('\n')
        .slice(-1)[0].length;
    return {
        methodToken: methodToken,
        closureCodeBlock: trimCurlyBrackets(tokens.slice(-1)[0])
            .split('\n')
            .map(function (lineOfCode, i) { return ({
            lineOfCode: lineOfCode.trimStart(),
            relativeIndentation: i === 0 ? getIndentation(lineOfCode) : getIndentation(lineOfCode) - initialClosureCodeBlockIndentation,
        }); }),
    };
};
var parseCodeBlockToSyntaxTree = function (fullCode, shouldCalculateInitialHorizontalPosition) { return function (codeBlock) {
    var tokens = tokenizeOnTopLevelPunctuation(codeBlock);
    if (tokens.length === 1) {
        var token = tokens[0];
        if (isMethodInvocation(token)) {
            var _a = getMethodTokenAndArgumentTokensFromMethodInvocation(token), methodToken = _a.methodToken, argumentTokens = _a.argumentTokens;
            return {
                type: types_1.TokenType.Method,
                method: parseCodeBlockToSyntaxTree(fullCode)(methodToken),
                arguments: argumentTokens.map(parseCodeBlockToSyntaxTree(fullCode)),
            };
        }
        if (isClosureInvocation(token)) {
            var _b = getMethodTokenAndClosureCodeBlockFromClosureInvocation(token, fullCode), methodToken = _b.methodToken, closureCodeBlock = _b.closureCodeBlock;
            return {
                type: types_1.TokenType.Closure,
                method: parseCodeBlockToSyntaxTree(fullCode)(methodToken),
                closureCodeBlock: closureCodeBlock,
            };
        }
        if (isString(token)) {
            return {
                type: types_1.TokenType.String,
                string: token,
            };
        }
        return {
            type: types_1.TokenType.Word,
            word: token,
        };
    }
    return {
        type: types_1.TokenType.Traversal,
        steps: tokens.map(parseCodeBlockToSyntaxTree(fullCode)),
        initialHorizontalPosition: shouldCalculateInitialHorizontalPosition
            ? fullCode.substr(0, fullCode.indexOf(codeBlock)).split('\n').slice(-1)[0].length
            : 0,
    };
}; };
var parseNonGremlinCodeToSyntaxTree = function (code) { return ({
    type: types_1.TokenType.NonGremlinCode,
    code: code,
}); };
exports.parseNonGremlinCodeToSyntaxTree = parseNonGremlinCodeToSyntaxTree;
var parseToSyntaxTrees = function (code) {
    var queries = (0, extractGremlinQueries_1.extractGremlinQueries)(code);
    var _a = queries.reduce(function (state, query) {
        var indexOfQuery = state.remainingCode.indexOf(query);
        var nonGremlinCode = state.remainingCode.substr(0, indexOfQuery);
        return {
            syntaxTrees: __spreadArray(__spreadArray([], state.syntaxTrees, true), [
                (0, exports.parseNonGremlinCodeToSyntaxTree)(nonGremlinCode),
                parseCodeBlockToSyntaxTree(code, true)(query),
            ], false),
            remainingCode: state.remainingCode.substr(indexOfQuery + query.length),
        };
    }, { syntaxTrees: [], remainingCode: code }), syntaxTrees = _a.syntaxTrees, remainingCode = _a.remainingCode;
    if (!remainingCode)
        return syntaxTrees;
    return __spreadArray(__spreadArray([], syntaxTrees, true), [(0, exports.parseNonGremlinCodeToSyntaxTree)(remainingCode)], false);
};
exports.parseToSyntaxTrees = parseToSyntaxTrees;
