"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.SystemVerilogSymbol = void 0;
const node_1 = require("vscode-languageserver/node");
const genutils_1 = require("./genutils");
class SystemVerilogSymbol {
    constructor(name, defLocations, symLocation, containers, type) {
        this.name = name;
        this.defLocations = defLocations;
        this.symLocation = symLocation;
        this.containers = containers;
        this.type = type;
    }
    static _toRangeJSON(range) {
        return [[range.start.line, range.start.character], [range.end.line, range.end.character]];
    }
    static _isRangeJSON(rangeJSON) {
        return (Array.isArray(rangeJSON[0])) && (typeof rangeJSON[0][0] === "number");
    }
    static _toRange(rangeJSON) {
        return node_1.Range.create(rangeJSON[0][0], rangeJSON[0][1], rangeJSON[1][0], rangeJSON[1][1]);
    }
    static _defLocationsToJSON(defLocations) {
        let defLocationsJSON;
        if (Array.isArray(defLocations)) {
            defLocationsJSON = [];
            for (let defLocation of defLocations) {
                if (typeof defLocation === "string") {
                    defLocationsJSON.push(defLocation);
                }
                else {
                    defLocationsJSON.push(SystemVerilogSymbol._toRangeJSON(defLocation));
                }
            }
        }
        else {
            defLocationsJSON = SystemVerilogSymbol._toRangeJSON(defLocations);
        }
        return defLocationsJSON;
    }
    static _symLocationToJSON(symLocation) {
        let symLocationJSON;
        if (typeof symLocation === "number") {
            symLocationJSON = symLocation;
        }
        else if (Array.isArray(symLocation)) {
            symLocationJSON = [symLocation[0], SystemVerilogSymbol._toRangeJSON(symLocation[1])];
        }
        else {
            symLocationJSON = SystemVerilogSymbol._toRangeJSON(symLocation);
        }
        return symLocationJSON;
    }
    toJSON() {
        try {
            return [
                this.name,
                SystemVerilogSymbol._defLocationsToJSON(this.defLocations),
                SystemVerilogSymbol._symLocationToJSON(this.symLocation),
                this.containers,
                this.type
            ];
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return [
                this.name,
                [[undefined, undefined], [undefined, undefined]],
                [[undefined, undefined], [undefined, undefined]],
                this.containers,
                this.type
            ];
        }
    }
    static fromJSON(uri, jsonSymbol) {
        try {
            let defLocations;
            if (SystemVerilogSymbol._isRangeJSON(jsonSymbol[1])) {
                defLocations = SystemVerilogSymbol._toRange((jsonSymbol[1]));
            }
            else {
                defLocations = [];
                for (let defLocationJSON of jsonSymbol[1]) {
                    if (typeof defLocationJSON === "string") {
                        defLocations.push(defLocationJSON);
                    }
                    else {
                        defLocations.push(SystemVerilogSymbol._toRange((defLocationJSON)));
                    }
                }
            }
            let symLocation;
            if (typeof jsonSymbol[2] === "number") {
                symLocation = jsonSymbol[2];
            }
            else if (SystemVerilogSymbol._isRangeJSON(jsonSymbol[2])) {
                symLocation = SystemVerilogSymbol._toRange((jsonSymbol[2]));
            }
            else {
                symLocation = [(jsonSymbol[2][0]), SystemVerilogSymbol._toRange((jsonSymbol[2][1]))];
            }
            return new SystemVerilogSymbol(jsonSymbol[0], defLocations, symLocation, jsonSymbol[3], jsonSymbol[4]);
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return undefined;
        }
    }
    getSymbolLocation(uri) {
        try {
            let symRange;
            let symURI = uri;
            if (typeof this.symLocation === "number") {
                if (!Array.isArray(this.defLocations)) {
                    genutils_1.ConnectionLogger.error(`Invalid symbol location (number) when defLocations is not an array`);
                }
                let l = 0;
                for (let i = 0; i < (this.defLocations).length; i++) {
                    if (typeof this.defLocations[i] === "string") {
                        symURI = this.defLocations[i];
                    }
                    else if (l == this.symLocation) {
                        symRange = this.defLocations[i];
                        break;
                    }
                    else {
                        l++;
                    }
                }
            }
            else if (Array.isArray(this.symLocation)) {
                symURI = this.symLocation[0];
                symRange = this.symLocation[1];
            }
            else {
                symRange = this.symLocation;
            }
            return node_1.Location.create(symURI, symRange);
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return undefined;
        }
    }
    toSymbolInformation(uri) {
        try {
            let symLocation = this.getSymbolLocation(uri);
            return node_1.SymbolInformation.create(this.name, getSymbolKind(this.type), symLocation.range, symLocation.uri, this.containers.length > 0 ? this.containers[this.containers.length - 1] : undefined);
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return undefined;
        }
    }
    overwrite(symbol) {
        this.name = symbol.name;
        this.defLocations = symbol.defLocations;
        this.symLocation = symbol.symLocation;
        this.containers = symbol.containers;
        this.type = symbol.type;
    }
    getDefinition(uri) {
        return SystemVerilogSymbol.getDefinitions(uri, [this])[0];
    }
    static getDefinitions(uri, symbols) {
        try {
            let documentMap = new Map();
            let currDocument;
            let definitions = [];
            for (let symbol of symbols) {
                let defLocations = Array.isArray(symbol.defLocations) ? [uri].concat((symbol.defLocations)) : [uri, (symbol.defLocations)];
                let definition = "";
                for (let i = 0; i < defLocations.length; i++) {
                    if (typeof defLocations[i] === "string") {
                        if (documentMap.has((defLocations[i]))) {
                            currDocument = documentMap.get((defLocations[i]));
                        }
                        else {
                            let data = genutils_1.fsReadFileSync(genutils_1.uriToPath((defLocations[i])));
                            currDocument = node_1.TextDocument.create((defLocations[i]), "SystemVerilog", 0, data.toString());
                            documentMap.set((defLocations[i]), currDocument);
                        }
                    }
                    else {
                        definition = definition.concat(currDocument.getText((defLocations[i])));
                    }
                }
                definitions.push(definition);
            }
            return definitions;
        }
        catch (error) {
            genutils_1.ConnectionLogger.error(error);
            return [];
        }
    }
    getSymbolDocumentPath(uri) {
        if (Array.isArray(this.defLocations) && (typeof this.defLocations[0] === "string")) {
            return this.defLocations[0];
        }
        return uri;
    }
}
exports.SystemVerilogSymbol = SystemVerilogSymbol;
function getSymbolKind(name) {
    if ((name === undefined) ||
        (name.length <= 0) ||
        (name[0] === '')) {
        return node_1.SymbolKind.Variable;
    }
    switch (name[0]) {
        case 'macro': return node_1.SymbolKind.Method;
        case 'parameter-port': return node_1.SymbolKind.Property;
        case 'port': return node_1.SymbolKind.Variable;
        case 'module': return node_1.SymbolKind.Class;
        case 'macromodule': return node_1.SymbolKind.Class;
        case 'interface': return node_1.SymbolKind.Interface;
        case 'program': return node_1.SymbolKind.Class;
        case 'package': return node_1.SymbolKind.Module;
        case 'function': return node_1.SymbolKind.Function;
        case 'task': return node_1.SymbolKind.Method;
        case 'parameter': return node_1.SymbolKind.Property;
        case 'localparam': return node_1.SymbolKind.Property;
        case 'modport': return node_1.SymbolKind.Field;
        case 'struct_union_member': return node_1.SymbolKind.Field;
        case 'struct': return node_1.SymbolKind.Struct;
        case 'union': return node_1.SymbolKind.Struct;
        case 'enum_member': return node_1.SymbolKind.EnumMember;
        case 'enum': return node_1.SymbolKind.Enum;
        case 'typedef': return node_1.SymbolKind.TypeParameter;
        case 'instance':
        case 'variable':
        default: return node_1.SymbolKind.Variable;
    }
}
