/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';
var __extends = (this && this.__extends) || (function () {
    var extendStatics = Object.setPrototypeOf ||
        ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
        function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
    return function (d, b) {
        extendStatics(d, b);
        function __() { this.constructor = d; }
        d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
    };
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
import { LRUCache, TernarySearchTree } from '../../../base/common/map';
import { IStorageService, StorageScope } from '../../../platform/storage/common/storage';
import { RunOnceScheduler } from '../../../base/common/async';
var Memory = /** @class */ (function () {
    function Memory() {
    }
    return Memory;
}());
export { Memory };
var NoMemory = /** @class */ (function (_super) {
    __extends(NoMemory, _super);
    function NoMemory() {
        return _super !== null && _super.apply(this, arguments) || this;
    }
    NoMemory.prototype.memorize = function (model, pos, item) {
        // no-op
    };
    NoMemory.prototype.select = function (model, pos, items) {
        return 0;
    };
    NoMemory.prototype.toJSON = function () {
        return undefined;
    };
    NoMemory.prototype.fromJSON = function () {
        //
    };
    return NoMemory;
}(Memory));
export { NoMemory };
var LRUMemory = /** @class */ (function (_super) {
    __extends(LRUMemory, _super);
    function LRUMemory() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._cache = new LRUCache(300, .66);
        _this._seq = 0;
        return _this;
    }
    LRUMemory.prototype.memorize = function (model, pos, item) {
        var label = item.suggestion.label;
        var key = model.getLanguageIdentifier().language + "/" + label;
        this._cache.set(key, {
            touch: this._seq++,
            type: item.suggestion.type,
            insertText: undefined
        });
    };
    LRUMemory.prototype.select = function (model, pos, items) {
        // in order of completions, select the first
        // that has been used in the past
        var word = model.getWordUntilPosition(pos).word;
        var res = 0;
        var seq = -1;
        if (word.length === 0) {
            for (var i = 0; i < items.length; i++) {
                var suggestion = items[i].suggestion;
                var key = model.getLanguageIdentifier().language + "/" + suggestion.label;
                var item = this._cache.get(key);
                if (item && item.touch > seq && item.type === suggestion.type) {
                    seq = item.touch;
                    res = i;
                }
            }
        }
        return res;
    };
    LRUMemory.prototype.toJSON = function () {
        var data = [];
        this._cache.forEach(function (value, key) {
            data.push([key, value]);
        });
        return data;
    };
    LRUMemory.prototype.fromJSON = function (data) {
        this._cache.clear();
        var seq = 0;
        for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
            var _a = data_1[_i], key = _a[0], value = _a[1];
            value.touch = seq;
            this._cache.set(key, value);
        }
        this._seq = this._cache.size;
    };
    return LRUMemory;
}(Memory));
export { LRUMemory };
var PrefixMemory = /** @class */ (function (_super) {
    __extends(PrefixMemory, _super);
    function PrefixMemory() {
        var _this = _super !== null && _super.apply(this, arguments) || this;
        _this._trie = TernarySearchTree.forStrings();
        _this._seq = 0;
        return _this;
    }
    PrefixMemory.prototype.memorize = function (model, pos, item) {
        var word = model.getWordUntilPosition(pos).word;
        var key = model.getLanguageIdentifier().language + "/" + word;
        this._trie.set(key, {
            type: item.suggestion.type,
            insertText: item.suggestion.insertText,
            touch: this._seq++
        });
    };
    PrefixMemory.prototype.select = function (model, pos, items) {
        var word = model.getWordUntilPosition(pos).word;
        if (!word) {
            return 0;
        }
        var key = model.getLanguageIdentifier().language + "/" + word;
        var item = this._trie.get(key);
        if (!item) {
            item = this._trie.findSubstr(key);
        }
        if (item) {
            for (var i = 0; i < items.length; i++) {
                var _a = items[i].suggestion, type = _a.type, insertText = _a.insertText;
                if (type === item.type && insertText === item.insertText) {
                    return i;
                }
            }
        }
        return 0;
    };
    PrefixMemory.prototype.toJSON = function () {
        var entries = [];
        this._trie.forEach(function (value, key) { return entries.push([key, value]); });
        // sort by last recently used (touch), then
        // take the top 200 item and normalize their
        // touch
        entries
            .sort(function (a, b) { return -(a[1].touch - b[1].touch); })
            .forEach(function (value, i) { return value[1].touch = i; });
        return entries.slice(0, 200);
    };
    PrefixMemory.prototype.fromJSON = function (data) {
        this._trie.clear();
        if (data.length > 0) {
            this._seq = data[0][1].touch + 1;
            for (var _i = 0, data_2 = data; _i < data_2.length; _i++) {
                var _a = data_2[_i], key = _a[0], value = _a[1];
                this._trie.set(key, value);
            }
        }
    };
    return PrefixMemory;
}(Memory));
export { PrefixMemory };
var SuggestMemories = /** @class */ (function () {
    function SuggestMemories(mode, _storageService) {
        var _this = this;
        this._storageService = _storageService;
        this._storagePrefix = 'suggest/memories';
        this._persistSoon = new RunOnceScheduler(function () { return _this._flush(); }, 3000);
        this.setMode(mode);
    }
    SuggestMemories.prototype.setMode = function (mode) {
        if (this._mode === mode) {
            return;
        }
        this._mode = mode;
        this._strategy = mode === 'recentlyUsedByPrefix' ? new PrefixMemory() : mode === 'recentlyUsed' ? new LRUMemory() : new NoMemory();
        try {
            var raw = this._storageService.get(this._storagePrefix + "/" + this._mode, StorageScope.WORKSPACE);
            this._strategy.fromJSON(JSON.parse(raw));
        }
        catch (e) {
            // things can go wrong with JSON...
        }
    };
    SuggestMemories.prototype.memorize = function (model, pos, item) {
        this._strategy.memorize(model, pos, item);
        this._persistSoon.schedule();
    };
    SuggestMemories.prototype.select = function (model, pos, items) {
        return this._strategy.select(model, pos, items);
    };
    SuggestMemories.prototype._flush = function () {
        var raw = JSON.stringify(this._strategy);
        this._storageService.store(this._storagePrefix + "/" + this._mode, raw, StorageScope.WORKSPACE);
    };
    SuggestMemories = __decorate([
        __param(1, IStorageService)
    ], SuggestMemories);
    return SuggestMemories;
}());
export { SuggestMemories };
