"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemVerilogDefinitionProvider = void 0;
const node_1 = require("vscode-languageserver/node");
const genutils_1 = require("./genutils");
class SystemVerilogDefinitionProvider {
    constructor(indexer) {
        this._indexer = indexer;
    }
    _findNamedArg(tokenNum, svtokens) {
        let scopeDepth = svtokens[tokenNum].scopes.length - 1;
        if ((tokenNum == 0) || (scopeDepth == 0)) {
            return [undefined, undefined];
        }
        let dotLocation;
        for (let i = tokenNum - 1; i >= 0; i--) {
            if (svtokens[i].text == ".") {
                dotLocation = i;
                break;
            }
            else if (svtokens[i].scopes[svtokens[i].scopes.length - 1] != "meta.whitespace.systemverilog") {
                return [undefined, undefined];
            }
        }
        if (dotLocation == undefined) {
            return [undefined, undefined];
        }
        for (let i = 0; i < svtokens[tokenNum].scopes.length; i++) {
            if (!svtokens[tokenNum].scopes[i].startsWith("identifier.") &&
                (svtokens[tokenNum].scopes[i] != "parantheses.block.systemverilog") &&
                (svtokens[tokenNum].scopes[i] != "case.body.systemverilog") &&
                (svtokens[tokenNum].scopes[i] != "begin.block.systemverilog") &&
                (svtokens[tokenNum].scopes[i] != "generate.block.systemverilog") &&
                (svtokens[tokenNum].scopes[i] != "source.systemverilog")) {
                for (let j = tokenNum - 1; j >= 0; j--) {
                    if (svtokens[j].scopes[scopeDepth - 1].startsWith("identifier.")) {
                        return [svtokens[j].text, true];
                    }
                }
                return [undefined, undefined];
            }
        }
        let instNameFound = false;
        let openParenFound = false;
        let hashOpenParenFound = false;
        for (let i = dotLocation - 1; i >= 0; i--) {
            if (svtokens[i].scopes[scopeDepth - 1].startsWith("identifier.")) {
                if (instNameFound || hashOpenParenFound) {
                    return [svtokens[i].text, false];
                }
                else {
                    instNameFound = true;
                }
            }
            else if ((svtokens[i].text == "#") && openParenFound) {
                hashOpenParenFound = true;
            }
            else if (!openParenFound && (svtokens[i].text == "(")) {
                openParenFound = true;
            }
            else if (!svtokens[i].scopes[svtokens[i].scopes.length - 1].startsWith("comment.") &&
                (svtokens[i].scopes[svtokens[i].scopes.length - 1] != "meta.whitespace.systemverilog")) {
                openParenFound = false;
            }
        }
        return [undefined, undefined];
    }
    _getIncludeFileName(tokens, tokenNum) {
        let startTokenNum;
        for (let i = tokenNum; i >= 0; i--) {
            let scope = tokens[i].scopes[tokens[i].scopes.length - 1];
            if (scope == "string.begin.systemverilog") {
                startTokenNum = i;
                break;
            }
        }
        if (startTokenNum === undefined) {
            return undefined;
        }
        let endTokenNum;
        for (let i = tokenNum; i < tokens.length; i++) {
            let scope = tokens[i].scopes[tokens[i].scopes.length - 1];
            if (scope == "string.end.systemverilog") {
                endTokenNum = i;
                break;
            }
        }
        if (endTokenNum === undefined) {
            return undefined;
        }
        return tokens.slice(startTokenNum + 1, endTokenNum).map(t => t.text).join('');
    }
    _getDefinition(document, position, includeUserDefines, checkPrevPosition = false) {
        let svtokens = this._indexer.getSystemVerilogCompletionTokens(document.uri);
        let extTokenNums = this._indexer.getSystemVerilogCompletionTokenNumber(document, position.line, position.character + 1);
        let tokenNum = extTokenNums[1];
        if (tokenNum == undefined) {
            return [undefined, undefined];
        }
        let scope = svtokens[tokenNum].scopes[svtokens[tokenNum].scopes.length - 1];
        let parentScope = svtokens[tokenNum].scopes.length > 1 ? svtokens[tokenNum].scopes[svtokens[tokenNum].scopes.length - 2] : undefined;
        if (scope.startsWith("macro.")) {
            let defText = svtokens[tokenNum].text.slice(1).replace(/\s*\($/, "");
            let result = this._indexer.getMacros(document.uri, defText);
            if (result.length > 0) {
                return [result[0][0], result[0][1][0]];
            }
            if (includeUserDefines) {
                let userDefineNum = this._indexer.findUserDefine(defText);
                if (userDefineNum >= 0) {
                    //DBG ConnectionLogger.log(`DEBUG: HERE with ${userDefineNum} for ${defText}`);
                    return ["", userDefineNum];
                }
            }
            return [undefined, undefined];
        }
        else if (scope.startsWith("identifier.hierarchical.") || (extTokenNums[0] != tokenNum)) {
            let filePath;
            let symbol;
            let idTokens = svtokens.slice(extTokenNums[0], tokenNum + 1);
            [filePath, symbol] = this._indexer.getHierarchicalSymbol(document.uri, this._indexer.getHierParts(idTokens.map(t => t.text).join(''), idTokens, document.offsetAt(position) - svtokens[extTokenNums[0]].index));
            if (symbol != undefined) {
                return [filePath, symbol];
            }
        }
        else if (parentScope == "string.body.systemverilog") {
            let incFileName = this._getIncludeFileName(svtokens, tokenNum);
            if (incFileName == undefined) {
                return [undefined, undefined];
            }
            return this._indexer.getIncFilePathAndSymbol(incFileName);
        }
        else if (!scope.startsWith("identifier.")) {
            if (checkPrevPosition && (position.character > 0)) {
                return this._getDefinition(document, node_1.Position.create(position.line, position.character - 1), includeUserDefines, false);
            }
            return [undefined, undefined];
        }
        let containerName;
        let isRoutine;
        [containerName, isRoutine] = this._findNamedArg(tokenNum, svtokens);
        if ((containerName == undefined) || (isRoutine == undefined)) {
            let symText = svtokens[tokenNum].text;
            if (scope.startsWith("identifier.scoped.")) {
                let endPos = document.offsetAt(position) - svtokens[tokenNum].index;
                endPos = svtokens[tokenNum].text.indexOf("::", endPos > 0 ? endPos - 1 : 0);
                endPos = endPos < 0 ? svtokens[tokenNum].text.length : endPos;
                symText = svtokens[tokenNum].text.slice(0, endPos);
                symText = symText.replace(/::\*$/, '');
                if (symText == "*") {
                    return [undefined, undefined];
                }
                else if (symText.indexOf("::") < 0) {
                    return this._indexer.getPackageSymbol(symText);
                }
            }
            let filePath;
            let symbol;
            [filePath, symbol] = scope.startsWith("identifier.scoped.") ? this._indexer.findSymbol(document.uri, symText) : this._indexer.findScopedSymbol(document.uri, symText, position);
            if ((filePath == undefined) || (symbol == undefined)) {
                symbol = this._indexer.getContainerSymbol(svtokens[tokenNum].text);
                if (symbol == undefined) {
                    return this._indexer.getPackageSymbol(svtokens[tokenNum].text);
                }
                filePath = this._indexer.getInstFilePath(svtokens[tokenNum].text);
                if (filePath == undefined) {
                    return [undefined, undefined];
                }
                return [genutils_1.pathToUri(filePath), symbol];
            }
            return [filePath, symbol];
        }
        else if (isRoutine) {
            let filePath;
            let symbol;
            let containerSymbolsInfo;
            [filePath, symbol, containerSymbolsInfo] = this._indexer.getContainerInfo(document.uri, containerName);
            if ((filePath == undefined) || (symbol == undefined) || (containerSymbolsInfo == undefined) ||
                (containerSymbolsInfo.symbolsInfo == undefined)) {
                return [undefined, undefined];
            }
            let argSymbols = containerSymbolsInfo.symbolsInfo;
            let filteredArgSymbols = argSymbols.filter(sym => { return sym.name == svtokens[tokenNum].text; });
            if (filteredArgSymbols.length > 0) {
                return [filePath, filteredArgSymbols[0]];
            }
            return [undefined, undefined];
        }
        else {
            let filePath;
            let symbol;
            let containerSymbolsInfo;
            let instFilePath = this._indexer.getInstFilePath(containerName);
            if (instFilePath == undefined) {
                return [undefined, undefined];
            }
            [filePath, symbol, containerSymbolsInfo] = this._indexer.getContainerInfo(genutils_1.pathToUri(instFilePath), containerName);
            if ((filePath == undefined) || (symbol == undefined) || (containerSymbolsInfo == undefined) ||
                (containerSymbolsInfo.symbolsInfo == undefined)) {
                return [undefined, undefined];
            }
            let containerSymbols = containerSymbolsInfo.symbolsInfo;
            let filteredContainerSymbols = containerSymbols.filter(sym => { return (sym.name == svtokens[tokenNum].text) && ((sym.type[0] == "port") || (sym.type[0] == "parameter-port")); });
            if (filteredContainerSymbols.length > 0) {
                return [filePath, filteredContainerSymbols[0]];
            }
            return [undefined, undefined];
        }
    }
    getDefinitionSymbolLocation(document, position) {
        try {
            let symbolInfo = this._getDefinition(document, position, false, true);
            if (symbolInfo[0] == undefined) {
                return Promise.resolve([]);
            }
            if (symbolInfo[0] == "") {
                return Promise.resolve([node_1.Location.create("", node_1.Range.create(symbolInfo[1], 0, 0, 0))]);
            }
            return Promise.resolve([(symbolInfo[1]).getSymbolLocation(symbolInfo[0])]);
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return Promise.resolve([]);
        }
    }
    getDefinitionText(document, position) {
        try {
            let symbolInfo = this._getDefinition(document, position, true);
            if (symbolInfo[0] == undefined) {
                return [undefined, undefined];
            }
            let header;
            let code;
            if (symbolInfo[0] == "") {
                header = 'User Define';
                code = this._indexer.getUserDefine((symbolInfo[1]));
            }
            else if ((typeof symbolInfo[1] !== 'number') && ((symbolInfo[1]).type.indexOf("includefile") >= 0)) {
                header = (symbolInfo[1]).name;
                code = '';
            }
            else {
                header = (document.uri == symbolInfo[0]) ? '' : `File: ${genutils_1.uriToPath(symbolInfo[0])}`;
                code = (symbolInfo[1]).getDefinition(symbolInfo[0]);
            }
            let trimLength = 0;
            let codeLines = code.split(/\r?\n/).map((codeLine, i) => {
                let lineTrimLength = codeLine.search(/\S/);
                lineTrimLength = (lineTrimLength < 0) ? 0 : lineTrimLength;
                ;
                if (i == 0) {
                    trimLength = lineTrimLength;
                }
                let actTrimLength = (trimLength < lineTrimLength) ? trimLength : lineTrimLength;
                return codeLine.slice(actTrimLength);
            });
            return [header, codeLines];
        }
        catch (error) {
            // ConnectionLogger.error(error); // Too much noise in VSCode console
            return [undefined, undefined];
        }
    }
}
exports.SystemVerilogDefinitionProvider = SystemVerilogDefinitionProvider;
