"use strict";
// This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
Object.defineProperty(exports, "__esModule", { value: true });
exports.ThrottleGroup = exports.ThrottledStream = void 0;
const stream_1 = require("stream");
const limiter_1 = require("limiter");
// throttled stream
class ThrottledStream extends stream_1.Transform {
    group;
    sent = 0;
    lastSpeed = 0;
    lastSpeedTime = Date.now();
    totalSent = 0; // total sent over connection, since connection can be re-used for multiple requests
    constructor(group, copyStats) {
        super();
        this.group = group;
        if (!copyStats)
            return;
        this.sent = copyStats.sent;
        this.totalSent = copyStats.totalSent;
        this.lastSpeedTime = copyStats.lastSpeedTime;
        this.lastSpeed = copyStats.lastSpeed;
    }
    async _transform(chunk, encoding, done) {
        let pos = 0;
        while (1) {
            let n = this.group.suggestChunkSize();
            const slice = chunk.slice(pos, pos + n);
            n = slice.length;
            if (!n) // we're done here
                return done();
            try {
                await this.group.consume(n);
                this.push(slice);
                this.sent += n;
                this.totalSent += n;
                pos += n;
                this.emit('sent', n);
            }
            catch (e) {
                done(e);
                return;
            }
        }
    }
    // @return kBs
    getSpeed() {
        const now = Date.now();
        const past = now - this.lastSpeedTime;
        if (past >= 1000) { // recalculate?
            this.lastSpeedTime = now;
            this.lastSpeed = this.sent / past;
            this.sent = 0;
        }
        return this.lastSpeed;
    }
    getBytesSent() {
        return this.totalSent;
    }
}
exports.ThrottledStream = ThrottledStream;
class ThrottleGroup {
    parent;
    bucket;
    constructor(kBs, parent) {
        this.parent = parent;
        this.bucket = this.updateLimit(kBs); // assignment is redundant and yet the best way I've found to shut up typescript
    }
    // @return kBs
    getLimit() {
        return this.bucket.bucketSize / 1000;
    }
    updateLimit(kBs) {
        if (kBs < 0)
            throw Error('invalid bytesPerSecond');
        kBs *= 1000;
        return this.bucket = new limiter_1.TokenBucket({
            bucketSize: kBs,
            tokensPerInterval: kBs,
            interval: 'second',
        });
    }
    suggestChunkSize() {
        let b = this.bucket;
        b.parentBucket = this.parent?.bucket;
        let min = b.bucketSize;
        while (b = b.parentBucket)
            min = Math.min(min, b.bucketSize);
        return min / 10;
    }
    consume(n) {
        return this.bucket.removeTokens(n);
    }
}
exports.ThrottleGroup = ThrottleGroup;
