"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const coc_nvim_1 = require("coc.nvim");
const fs = tslib_1.__importStar(require("fs"));
const net = tslib_1.__importStar(require("net"));
const os = tslib_1.__importStar(require("os"));
const path = tslib_1.__importStar(require("path"));
const vscode_languageserver_protocol_1 = require("vscode-languageserver-protocol");
const vscode_uri_1 = tslib_1.__importDefault(require("vscode-uri"));
const commands_1 = require("./commands");
const javaServerStarter_1 = require("./javaServerStarter");
const plugin_1 = require("./plugin");
const protocol_1 = require("./protocol");
const requirements_1 = require("./requirements");
let oldConfig;
let languageClient;
const cleanWorkspaceFileName = '.cleanWorkspace';
function activate(context) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let requirements;
        try {
            requirements = yield requirements_1.resolveRequirements();
        }
        catch (e) {
            let res = yield coc_nvim_1.workspace.showQuickpick(['Yes', 'No'], `${e.message}, ${e.label}?`);
            if (res == 0) {
                coc_nvim_1.commands.executeCommand(commands_1.Commands.OPEN_BROWSER, e.openUrl);
            }
            return;
        }
        let progressItem = coc_nvim_1.workspace.createStatusBarItem(9, { progress: true });
        progressItem.text = 'jdt starting';
        progressItem.show();
        let storagePath = getTempWorkspace();
        let workspacePath = path.resolve(storagePath + '/jdt_ws');
        // Options to control the language client
        let clientOptions = {
            // Register the server for java
            documentSelector: [
                { scheme: 'file', language: 'java' },
                { scheme: 'jdt', language: 'java' },
                { scheme: 'untitled', language: 'java' }
            ],
            synchronize: {
                configurationSection: 'java',
                // Notify the server about file changes to .java and project/build files contained in the workspace
                fileEvents: [
                    coc_nvim_1.workspace.createFileSystemWatcher('**/*.java'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/pom.xml'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/*.gradle'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/.project'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/.classpath'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/settings/*.prefs'),
                    coc_nvim_1.workspace.createFileSystemWatcher('**/src/**')
                ],
            },
            initializationOptions: {
                bundles: plugin_1.collectionJavaExtensions(),
                workspaceFolders: null,
                settings: { java: getJavaConfiguration() },
                extendedClientCapabilities: {
                    progressReportProvider: getJavaConfiguration().get('progressReports.enabled'),
                    classFileContentsSupport: true
                }
            },
            revealOutputChannelOn: coc_nvim_1.RevealOutputChannelOn.Never
        };
        oldConfig = getJavaConfiguration();
        let serverOptions;
        let port = process.env['SERVER_PORT'];
        if (!port) {
            let lsPort = process.env['JDTLS_CLIENT_PORT'];
            if (!lsPort) {
                serverOptions = javaServerStarter_1.prepareExecutable(requirements, workspacePath, getJavaConfiguration());
            }
            else {
                serverOptions = () => {
                    let socket = net.connect(lsPort);
                    let result = {
                        writer: socket,
                        reader: socket
                    };
                    return Promise.resolve(result);
                };
            }
        }
        else {
            // used during development
            serverOptions = javaServerStarter_1.awaitServerConnection.bind(null, port);
        }
        // Create the language client and start the client.
        languageClient = new coc_nvim_1.LanguageClient('java', 'Language Support for Java', serverOptions, clientOptions);
        languageClient.registerProposedFeatures();
        languageClient.onReady().then(() => {
            languageClient.onNotification(protocol_1.StatusNotification.type, report => {
                switch (report.type) {
                    case 'Started':
                        progressItem.hide();
                        let info = {
                            apiVersion: '0.1',
                            javaRequirement: requirements,
                        };
                        coc_nvim_1.workspace.showMessage('JDT Language Server started');
                        languageClient.info('JDT Language Server started', info);
                        context.logger.info(info);
                        break;
                    case 'Error':
                        progressItem.hide();
                        coc_nvim_1.workspace.showMessage(`JDT Language Server error ${report.message}`, 'error');
                        break;
                    case 'Starting':
                        // progressItem.show()
                        break;
                    case 'Message':
                        coc_nvim_1.workspace.showMessage(report.message);
                        break;
                }
            });
            languageClient.onNotification(protocol_1.ActionableNotification.type, notification => {
                let show = null;
                switch (notification.severity) {
                    case protocol_1.MessageType.Log:
                        show = logNotification;
                        break;
                    case protocol_1.MessageType.Info:
                        show = 'more';
                        break;
                    case protocol_1.MessageType.Warning:
                        show = 'warning';
                        break;
                    case protocol_1.MessageType.Error:
                        show = 'error';
                        break;
                }
                if (!show) {
                    return;
                }
                const titles = notification.commands.map(a => a.title);
                show(notification.message, ...titles).then(selection => {
                    for (let action of notification.commands) {
                        if (action.title === selection) {
                            let args = (action.arguments) ? action.arguments : [];
                            coc_nvim_1.commands.executeCommand(action.command, ...args);
                            break;
                        }
                    }
                });
            });
            languageClient.onRequest(protocol_1.ExecuteClientCommandRequest.type, params => {
                return coc_nvim_1.commands.executeCommand(params.command, ...params.arguments);
            });
            languageClient.onRequest(protocol_1.SendNotificationRequest.type, params => {
                return coc_nvim_1.commands.executeCommand(params.command, ...params.arguments);
            });
            coc_nvim_1.commands.registerCommand(commands_1.Commands.OPEN_OUTPUT, () => {
                languageClient.outputChannel.show();
            });
            coc_nvim_1.commands.registerCommand(commands_1.Commands.SHOW_JAVA_REFERENCES, (uri, position, locations) => {
                coc_nvim_1.commands.executeCommand(commands_1.Commands.SHOW_REFERENCES, vscode_uri_1.default.parse(uri), position, locations);
            }, null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.SHOW_JAVA_IMPLEMENTATIONS, (uri, position, locations) => {
                coc_nvim_1.commands.executeCommand(commands_1.Commands.SHOW_REFERENCES, vscode_uri_1.default.parse(uri), position, locations);
            }, null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.CONFIGURATION_UPDATE, uri => projectConfigurationUpdate(languageClient, uri), null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.IGNORE_INCOMPLETE_CLASSPATH, (_data) => setIncompleteClasspathSeverity('ignore'));
            coc_nvim_1.commands.registerCommand(commands_1.Commands.IGNORE_INCOMPLETE_CLASSPATH_HELP, (_data) => {
                coc_nvim_1.commands.executeCommand(commands_1.Commands.OPEN_BROWSER, vscode_uri_1.default.parse('https://github.com/redhat-developer/vscode-java/wiki/%22Classpath-is-incomplete%22-warning'));
            });
            coc_nvim_1.commands.registerCommand(commands_1.Commands.PROJECT_CONFIGURATION_STATUS, (uri, status) => setProjectConfigurationUpdate(languageClient, uri, status), null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.APPLY_WORKSPACE_EDIT, obj => {
                // tslint:disable-next-line:no-floating-promises
                applyWorkspaceEdit(obj);
            }, null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.EDIT_ORGANIZE_IMPORTS, () => tslib_1.__awaiter(this, void 0, void 0, function* () {
                let document = yield coc_nvim_1.workspace.document;
                if (document.filetype !== 'java') {
                    return;
                }
                coc_nvim_1.commands.executeCommand(commands_1.Commands.EXECUTE_WORKSPACE_COMMAND, commands_1.Commands.EDIT_ORGANIZE_IMPORTS, document.uri);
            }));
            coc_nvim_1.commands.registerCommand(commands_1.Commands.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => {
                const params = {
                    command,
                    arguments: rest
                };
                return languageClient.sendRequest(vscode_languageserver_protocol_1.ExecuteCommandRequest.type, params);
            }, null, true);
            coc_nvim_1.commands.registerCommand(commands_1.Commands.COMPILE_WORKSPACE, (isFullCompile) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                if (typeof isFullCompile !== 'boolean') {
                    const idx = yield coc_nvim_1.workspace.showQuickpick(['Incremental', 'Full'], 'please choose compile type:');
                    isFullCompile = idx != 0;
                }
                coc_nvim_1.workspace.showMessage('Compiling workspace...');
                const start = new Date().getTime();
                const res = yield Promise.resolve(languageClient.sendRequest(protocol_1.CompileWorkspaceRequest.type, isFullCompile));
                const elapsed = ((new Date().getTime() - start) / 1000).toFixed(1);
                if (res === protocol_1.CompileWorkspaceStatus.SUCCEED) {
                    coc_nvim_1.workspace.showMessage(`Compile done, used ${elapsed}s.`);
                }
                else {
                    coc_nvim_1.workspace.showMessage('Compile error!', 'error');
                }
            }));
            let provider = {
                onDidChange: null,
                provideTextDocumentContent: (uri, token) => tslib_1.__awaiter(this, void 0, void 0, function* () {
                    let content = yield Promise.resolve(languageClient.sendRequest(protocol_1.ClassFileContentsRequest.type, { uri: uri.toString() }, token));
                    content = content || '';
                    let { nvim } = coc_nvim_1.workspace;
                    yield nvim.command('setfiletype java');
                    return content;
                })
            };
            coc_nvim_1.workspace.registerTextDocumentContentProvider('jdt', provider);
        }, e => {
            context.logger.error(e.message);
        });
        let cleanWorkspaceExists = fs.existsSync(path.join(workspacePath, cleanWorkspaceFileName));
        if (cleanWorkspaceExists) {
            try {
                deleteDirectory(workspacePath);
            }
            catch (error) {
                coc_nvim_1.workspace.showMessage('Failed to delete ' + workspacePath + ': ' + error, 'error');
            }
        }
        coc_nvim_1.workspace.showMessage(`JDT Language Server starting at ${coc_nvim_1.workspace.root}`);
        languageClient.start();
        // Register commands here to make it available even when the language client fails
        coc_nvim_1.commands.registerCommand(commands_1.Commands.OPEN_SERVER_LOG, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield openServerLogFile(workspacePath); }));
        let extensionPath = context.extensionPath;
        coc_nvim_1.commands.registerCommand(commands_1.Commands.OPEN_FORMATTER, () => tslib_1.__awaiter(this, void 0, void 0, function* () { return openFormatter(extensionPath); }));
        coc_nvim_1.commands.registerCommand(commands_1.Commands.CLEAN_WORKSPACE, () => cleanWorkspace(workspacePath));
        context.subscriptions.push(onConfigurationChange());
        context.subscriptions.push(coc_nvim_1.services.registLanguageClient(languageClient));
    });
}
exports.activate = activate;
function logNotification(message, ..._items) {
    // tslint:disable-next-line:no-console
    console.log(message);
}
function setIncompleteClasspathSeverity(severity) {
    const config = getJavaConfiguration();
    const section = 'errors.incompleteClasspath.severity';
    config.update(section, severity, true);
    // tslint:disable-next-line:no-console
    console.log(section + ' globally set to ' + severity);
}
function projectConfigurationUpdate(languageClient, uri) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let resource = uri ? uri.toString() : null;
        if (!resource) {
            let document = yield coc_nvim_1.workspace.document;
            resource = document.uri;
        }
        if (!resource) {
            coc_nvim_1.workspace.showMessage('No Java project to update!', 'warning');
            return;
        }
        if (isJavaConfigFile(resource)) {
            languageClient.sendNotification(protocol_1.ProjectConfigurationUpdateRequest.type, {
                uri: resource
            });
        }
    });
}
function setProjectConfigurationUpdate(languageClient, uri, status) {
    const config = getJavaConfiguration();
    const section = 'configuration.updateBuildConfiguration';
    const st = protocol_1.FeatureStatus[status];
    config.update(section, st);
    // tslint:disable-next-line:no-console
    console.log(section + ' set to ' + st);
    if (status !== protocol_1.FeatureStatus.disabled) {
        // tslint:disable-next-line:no-floating-promises
        projectConfigurationUpdate(languageClient, uri);
    }
}
function isJavaConfigFile(path) {
    return path.endsWith('pom.xml') || path.endsWith('.gradle');
}
function onConfigurationChange() {
    return coc_nvim_1.workspace.onDidChangeConfiguration((_params) => tslib_1.__awaiter(this, void 0, void 0, function* () {
        let newConfig = getJavaConfiguration();
        if (hasJavaConfigChanged(oldConfig, newConfig)) {
            let msg = 'Java Language Server configuration changed, please restart VS Code.';
            let action = 'Restart Now';
            let restartId = commands_1.Commands.RELOAD_WINDOW;
            oldConfig = newConfig;
            let res = yield coc_nvim_1.workspace.showPrompt(`${msg}, ${action}?`);
            if (res) {
                coc_nvim_1.commands.executeCommand(restartId);
            }
        }
    }));
}
function hasJavaConfigChanged(oldConfig, newConfig) {
    return hasConfigKeyChanged('home', oldConfig, newConfig)
        || hasConfigKeyChanged('jdt.ls.vmargs', oldConfig, newConfig)
        || hasConfigKeyChanged('progressReports.enabled', oldConfig, newConfig);
}
function hasConfigKeyChanged(key, oldConfig, newConfig) {
    return oldConfig.get(key) !== newConfig.get(key);
}
function getTempWorkspace() {
    return path.resolve(os.tmpdir(), 'vscodesws_' + makeRandomHexString(5));
}
function makeRandomHexString(length) {
    let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
    let result = '';
    for (let i = 0; i < length; i++) {
        let idx = Math.floor(chars.length * Math.random());
        result += chars[idx];
    }
    return result;
}
function getJavaConfiguration() {
    return coc_nvim_1.workspace.getConfiguration('java');
}
function cleanWorkspace(workspacePath) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let res = yield coc_nvim_1.workspace.showPrompt('Are you sure you want to clean the Java language server workspace?');
        if (res) {
            const file = path.join(workspacePath, cleanWorkspaceFileName);
            fs.closeSync(fs.openSync(file, 'w'));
            coc_nvim_1.commands.executeCommand(commands_1.Commands.RELOAD_WINDOW);
        }
    });
}
function deleteDirectory(dir) {
    if (fs.existsSync(dir)) {
        fs.readdirSync(dir).forEach(child => {
            let entry = path.join(dir, child);
            if (fs.lstatSync(entry).isDirectory()) {
                deleteDirectory(entry);
            }
            else {
                fs.unlinkSync(entry);
            }
        });
        fs.rmdirSync(dir);
    }
}
function openServerLogFile(workspacePath) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let serverLogFile = path.join(workspacePath, '.metadata', '.log');
        if (!serverLogFile) {
            coc_nvim_1.workspace.showMessage('Java Language Server has not started logging.', 'warning');
            return;
        }
        yield coc_nvim_1.workspace.openResource(vscode_uri_1.default.file(serverLogFile).toString());
    });
}
function openFormatter(extensionPath) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let defaultFormatter = path.join(extensionPath, 'formatters', 'eclipse-formatter.xml');
        let formatterUrl = getJavaConfiguration().get('format.settings.url');
        if (formatterUrl && formatterUrl.length > 0) {
            if (isRemote(formatterUrl)) {
                coc_nvim_1.commands.executeCommand(commands_1.Commands.OPEN_BROWSER, vscode_uri_1.default.parse(formatterUrl));
            }
            else {
                let document = getPath(formatterUrl);
                if (document && fs.existsSync(document)) {
                    return openDocument(extensionPath, document, defaultFormatter, null);
                }
            }
        }
        let global = true;
        let fileName = formatterUrl || 'eclipse-formatter.xml';
        let file;
        let relativePath;
        let root = path.join(extensionPath, '..', 'redhat.java');
        if (!fs.existsSync(root)) {
            fs.mkdirSync(root);
        }
        file = path.join(root, fileName);
        if (!fs.existsSync(file)) {
            yield addFormatter(extensionPath, file, defaultFormatter, relativePath);
        }
        else {
            if (formatterUrl) {
                getJavaConfiguration().update('format.settings.url', (relativePath !== null ? relativePath : file), global);
                yield openDocument(extensionPath, file, file, defaultFormatter);
            }
            else {
                yield addFormatter(extensionPath, file, defaultFormatter, relativePath);
            }
        }
    });
}
function getPath(f) {
    if (coc_nvim_1.workspace.workspaceFolder && !path.isAbsolute(f)) {
        let file = path.resolve(vscode_uri_1.default.parse(coc_nvim_1.workspace.workspaceFolder.uri).fsPath, f);
        if (fs.existsSync(file)) {
            return file;
        }
    }
    else {
        return path.resolve(f);
    }
    return null;
}
function openDocument(_extensionPath, formatterUrl, _defaultFormatter, _relativePath) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        if (!formatterUrl || !fs.existsSync(formatterUrl)) {
            coc_nvim_1.workspace.showMessage('Could not open Formatter Settings file', 'error');
            return;
        }
        yield coc_nvim_1.workspace.openResource(vscode_uri_1.default.file(formatterUrl).toString());
    });
}
function isRemote(f) {
    return f !== null && f.startsWith('http:/') || f.startsWith('https:/');
}
function applyWorkspaceEdit(edit) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        if (edit) {
            try {
                yield coc_nvim_1.workspace.applyEdit(edit);
            }
            catch (e) {
                coc_nvim_1.workspace.showMessage(`applyEdit error: ${e.message}`, 'error');
                return;
            }
            let doc = yield coc_nvim_1.workspace.document;
            if (edit.changes && doc.uri == Object.keys(edit.changes)[0]) {
                yield coc_nvim_1.workspace.nvim.call('CocAction', 'format');
            }
        }
    });
}
function addFormatter(extensionPath, formatterUrl, defaultFormatter, relativePath) {
    return tslib_1.__awaiter(this, void 0, void 0, function* () {
        let value = relativePath ? relativePath : formatterUrl;
        let f = yield coc_nvim_1.workspace.nvim.call('input', ['please enter URL or Path:', value]);
        let global = true;
        if (isRemote(f)) {
            coc_nvim_1.commands.executeCommand(commands_1.Commands.OPEN_BROWSER, vscode_uri_1.default.parse(f));
            getJavaConfiguration().update('format.settings.url', f, global);
        }
        else {
            if (!path.isAbsolute(f)) {
                let fileName = f;
                let root = path.join(extensionPath, '..', 'redhat.java');
                if (!fs.existsSync(root)) {
                    fs.mkdirSync(root);
                }
                f = path.join(root, fileName);
            }
            else {
                relativePath = null;
            }
            getJavaConfiguration().update('format.settings.url', (relativePath !== null ? relativePath : f), global);
            if (!fs.existsSync(f)) {
                let name = relativePath !== null ? relativePath : f;
                let msg = '\'' + name + '\' does not exist. Do you want to create it?';
                let res = yield coc_nvim_1.workspace.showPrompt(msg);
                if (res) {
                    fs.createReadStream(defaultFormatter)
                        .pipe(fs.createWriteStream(f))
                        .on('finish', () => openDocument(extensionPath, f, defaultFormatter, relativePath));
                }
            }
            else {
                yield openDocument(extensionPath, f, defaultFormatter, relativePath);
            }
        }
    });
}
//# sourceMappingURL=index.js.map