"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LfuMap = void 0;
const SizedMap_1 = require("./SizedMap");
class LfuMap extends SizedMap_1.SizedMap {
    constructor() {
        super(...arguments);
        this.queue = {};
        this.frequencies = { 1: { index: 1, count: 0 } };
        this.leastFrequent = this.frequencies[1];
    }
    has(key) {
        const node = this.map.get(key);
        if (node) {
            this.removeNode(node);
            this.moveToTop(node);
            return true;
        }
        return false;
    }
    get(key, _default) {
        const node = this.map.get(key);
        if (node) {
            this.removeNode(node);
            this.moveToTop(node);
            return node.value;
        }
        return _default;
    }
    set(key, value) {
        if (!this.limit)
            return this;
        let node = this.map.get(key);
        if (node) {
            node.value = value;
            this.removeNode(node);
            this.moveToTop(node);
        }
        else {
            node = {
                key: key,
                value: value,
                used: 0,
            };
            if (this.isFull()) {
                super.delete(this.queuePop().key);
            }
            this.moveToTop(node);
            this.map.set(key, node);
        }
        return this;
    }
    delete(key) {
        const node = this.map.get(key);
        if (node) {
            this.removeNode(node);
            return super.delete(key);
        }
        return false;
    }
    clear() {
        this.queue = {};
        this.frequencies = { 1: { index: 1, count: 0 } };
        this.leastFrequent = this.frequencies[1];
        super.clear();
    }
    initQueue(used) {
        if (!this.queue[used]) {
            this.queue[used] = {};
        }
    }
    queuePop() {
        const min = this.leastFrequent.index;
        const node = this.queue[min].tail;
        this.removeNode(node);
        return node;
    }
    moveToTop(node) {
        const used = ++node.used;
        this.increaseFrequency(used);
        this.initQueue(used);
        node.next = this.queue[used].head;
        node.previous = undefined;
        if (this.queue[used].head) {
            this.queue[used].head.previous = node;
        }
        this.queue[used].head = node;
        if (!this.queue[used].tail) {
            this.queue[used].tail = this.queue[used].head;
        }
    }
    removeNode(node) {
        const used = node.used;
        this.decreaseFrequency(used);
        this.initQueue(used);
        if (node.previous) {
            node.previous.next = node.next;
        }
        else {
            this.queue[used].head = node.next;
        }
        if (node.next) {
            node.next.previous = node.previous;
        }
        else {
            this.queue[used].tail = node.previous;
        }
    }
    increaseFrequency(index) {
        if (!this.frequencies[index]) {
            this.frequencies[index] = { index: index, count: 0 };
            this.frequencies[index].next = this.frequencies[index - 1].next;
            this.frequencies[index].previous = this.frequencies[index - 1].count
                ? this.frequencies[index - 1]
                : this.frequencies[index - 1].previous;
        }
        if (!this.frequencies[index].count) {
            if (this.frequencies[index].previous) {
                this.frequencies[index].next = this.frequencies[index].previous.next;
                this.frequencies[index].previous.next = this.frequencies[index];
            }
            if (this.frequencies[index].next) {
                this.frequencies[index].previous = this.frequencies[index].next.previous;
                this.frequencies[index].next.previous = this.frequencies[index];
            }
        }
        this.frequencies[index].count++;
        if (!this.leastFrequent || index < this.leastFrequent.index) {
            this.leastFrequent = this.frequencies[index];
        }
    }
    decreaseFrequency(index) {
        this.frequencies[index].count--;
        if (!this.frequencies[index].count) {
            if (this.frequencies[index].previous) {
                this.frequencies[index].previous.next = this.frequencies[index].next;
            }
            if (this.frequencies[index].next) {
                this.frequencies[index].next.previous = this.frequencies[index].previous;
            }
        }
        if (!this.leastFrequent || !this.leastFrequent.count) {
            this.leastFrequent = this.leastFrequent.next;
        }
    }
}
exports.LfuMap = LfuMap;
