/*---------------------------------------------------------
 * Copyright (C) Microsoft Corporation. All rights reserved.
 *--------------------------------------------------------*/
'use strict';
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    __.prototype = b.prototype;
    d.prototype = new __();
};
define(["require", "exports", 'vs/nls', '../omnisharp', './abstractSupport', '../protocol', 'monaco'], function (require, exports, nls, omnisharp, abstractSupport_1, Protocol, monaco) {
    var Advisor = (function () {
        function Advisor(server) {
            this._packageRestoreCounter = 0;
            this._projectSourceFileCounts = Object.create(null);
            server.addListener(this);
        }
        Advisor.prototype.onOmnisharpServerEvent = function (kind, args) {
            switch (kind) {
                case omnisharp.Events.PackageRestoreStarted:
                    this._packageRestoreCounter += 1;
                    break;
                case omnisharp.Events.PackageRestoreFinished:
                    this._packageRestoreCounter -= 1;
                    break;
                case omnisharp.Events.ProjectChanged:
                    var info = args;
                    if (info.DnxProject && info.DnxProject.SourceFiles) {
                        this._projectSourceFileCounts[info.DnxProject.Path] = info.DnxProject.SourceFiles.length;
                    }
                    if (info.MsBuildProject && info.MsBuildProject.SourceFiles) {
                        this._projectSourceFileCounts[info.MsBuildProject.Path] = info.MsBuildProject.SourceFiles.length;
                    }
                    break;
                case 'stateChanged':
                    this._state = args;
                    break;
            }
        };
        Advisor.prototype.shouldValidateOpenFiles = function () {
            return this._isServerStarted()
                && !this._isRestoringPackages();
        };
        Advisor.prototype.shouldValidateClosedFiles = function () {
            return this._isServerStarted()
                && !this._isRestoringPackages()
                && !this._isHugeProject();
        };
        Advisor.prototype._isServerStarted = function () {
            return this._state === omnisharp.ServerState.Started;
        };
        Advisor.prototype._isRestoringPackages = function () {
            return this._packageRestoreCounter > 0;
        };
        Advisor.prototype._isHugeProject = function () {
            var sourceFileCount = 0;
            for (var key in this._projectSourceFileCounts) {
                sourceFileCount += this._projectSourceFileCounts[key];
                if (sourceFileCount > 1000) {
                    return true;
                }
            }
            return false;
        };
        return Advisor;
    })();
    exports.Advisor = Advisor;
    function createScheduler(timeout, callback, context) {
        var handle;
        function schedule() {
            clearTimeout(handle);
            handle = setTimeout(callback.apply(context), timeout);
        }
        return { schedule: schedule };
    }
    var DiagnosticsSupport = (function (_super) {
        __extends(DiagnosticsSupport, _super);
        function DiagnosticsSupport(modelService, markerService, server, validationAdvisor) {
            var _this = this;
            _super.call(this, modelService, server);
            this._callOnDispose = [];
            this._modelListeners = Object.create(null);
            this._queueForChangedFiles = Object.create(null);
            this._validationAdvisor = validationAdvisor;
            this._markerService = markerService;
            this._openDocumentsDiagnostics = createScheduler(1000, this._sendValidationRequestsForChangedFiles, this);
            this._closedDocumentsDiagnostics = createScheduler(3000, this._validateClosedSourceFiles, this);
            this._modelService.getModels().forEach(function (model) { return _this._onModelAdded(model); });
            this._modelService.onModelAdded.add(this._onModelAdded, this, this._callOnDispose);
            this._modelService.onModelRemoved.add(this._onModelRemoved, this, this._callOnDispose);
            this._callOnDispose.push(this.server().addListener(this));
        }
        DiagnosticsSupport.prototype.dispose = function () {
            while (this._callOnDispose.length) {
                this._callOnDispose.pop().dispose();
            }
        };
        DiagnosticsSupport.prototype._isInterestingModel = function (model) {
            return model.getModeId() === 'csharp' && !this.isInMemory(model.getURL());
        };
        DiagnosticsSupport.prototype._onModelAdded = function (model) {
            if (!this._isInterestingModel(model)) {
                return;
            }
            var resource = model.getURL().toString(), validator = this._validateModel.bind(this, resource);
            model.onContentChanged.add(validator);
            this._modelListeners[resource] = function () { return model.onContentChanged.remove(validator); };
            validator();
        };
        DiagnosticsSupport.prototype._onModelRemoved = function (model) {
            var key = model.getURL().toString();
            if (!this._modelListeners[key]) {
                return;
            }
            this._modelListeners[key]();
            delete this._modelListeners[key];
        };
        DiagnosticsSupport.prototype.onOmnisharpServerEvent = function (kind, args) {
            if (kind === omnisharp.Events.ProjectChanged || kind === omnisharp.Events.PackageRestoreFinished) {
                this._openDocumentsDiagnostics.schedule();
                this._closedDocumentsDiagnostics.schedule();
            }
        };
        DiagnosticsSupport.prototype._validateModel = function (resource) {
            this._queueForChangedFiles[resource] = true;
            this._openDocumentsDiagnostics.schedule();
        };
        DiagnosticsSupport.prototype._sendValidationRequestsForChangedFiles = function () {
            var _this = this;
            if (!this._validationAdvisor.shouldValidateOpenFiles()) {
                return;
            }
            // first all changed files, then all other open files
            var queue = Object.keys(this._queueForChangedFiles);
            this._modelService.getModels().forEach(function (model) {
                if (_this._queueForChangedFiles[model.getURL().toString()]) {
                    return;
                }
                if (!_this._isInterestingModel(model)) {
                    return;
                }
                queue.push(model.getURL().toString());
            });
            this._queueForChangedFiles = Object.create(null);
            // validate changed files and if nothing changed
            // in the meantime also validate the closed files
            var promises = queue.map(this._sendOneValidationRequest, this);
            monaco.Promise.join(promises)
                .then(function (_) { return _this._closedDocumentsDiagnostics.schedule(); })
                .done(undefined, function (err) { return console.error(err); });
        };
        DiagnosticsSupport.prototype._sendOneValidationRequest = function (external) {
            var _this = this;
            var resource = new monaco.URL(external);
            return this.server().makeRequest(Protocol.CodeCheck, {
                Filename: resource.fsPath
            }).then(function (res) {
                var data = res.QuickFixes.map(DiagnosticsSupport.asMarkerData);
                _this._markerService.remove('omnisharp/closed', [resource]);
                _this._markerService.changeOne('omnisharp/open', resource, data);
            });
        };
        DiagnosticsSupport.prototype._validateClosedSourceFiles = function () {
            var _this = this;
            if (!this._validationAdvisor.shouldValidateClosedFiles()) {
                return;
            }
            this.server().makeRequest(Protocol.CodeCheck, {}).then(function (res) {
                var data = [];
                for (var i = 0; i < res.QuickFixes.length && data.length <= 1000; i++) {
                    var quickFix = res.QuickFixes[i], resource = monaco.URI.file(quickFix.FileName);
                    if (_this._modelService.getModel(monaco.URL.fromUri(resource))) {
                        continue;
                    }
                    data.push({
                        resource: resource,
                        marker: DiagnosticsSupport.asMarkerData(quickFix)
                    });
                }
                _this._markerService.changeAll('omnisharp/closed', data);
            }).done(undefined, function (err) { return console.error(err); });
        };
        DiagnosticsSupport.asMarkerData = function (quickFix) {
            return {
                message: nls.localize('csharp.diagnostics', "{0} [{1}]", quickFix.Text, quickFix.Projects.map(function (n) { return omnisharp.asProjectLabel(n); }).join(', ')),
                severity: DiagnosticsSupport.severity(quickFix),
                startLineNumber: quickFix.Line,
                startColumn: quickFix.Column,
                endLineNumber: quickFix.EndLine,
                endColumn: quickFix.EndColumn
            };
        };
        DiagnosticsSupport.severity = function (quickFix) {
            return quickFix.LogLevel === 'Hidden'
                ? monaco.Services.Severity.Warning
                : monaco.Services.Severity.fromValue(quickFix.LogLevel);
        };
        return DiagnosticsSupport;
    })(abstractSupport_1.default);
    exports.DiagnosticsSupport = DiagnosticsSupport;
});
