/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/

"use strict";

const binarySearchBounds = require("./binarySearchBounds");

/** @typedef {(value: number) => void} Callback */

class ParallelismFactorCalculator {
	constructor() {
		/** @type {number[]} */
		this._rangePoints = [];
		/** @type {Callback[]} */
		this._rangeCallbacks = [];
	}

	/**
	 * @param {number} start range start
	 * @param {number} end range end
	 * @param {Callback} callback callback
	 * @returns {void}
	 */
	range(start, end, callback) {
		if (start === end) return callback(1);
		this._rangePoints.push(start);
		this._rangePoints.push(end);
		this._rangeCallbacks.push(callback);
	}

	calculate() {
		const segments = Array.from(new Set(this._rangePoints)).sort((a, b) =>
			a < b ? -1 : 1
		);
		const parallelism = segments.map(() => 0);
		const rangeStartIndices = [];
		for (let i = 0; i < this._rangePoints.length; i += 2) {
			const start = this._rangePoints[i];
			const end = this._rangePoints[i + 1];
			let idx = binarySearchBounds.eq(segments, start);
			rangeStartIndices.push(idx);
			do {
				parallelism[idx]++;
				idx++;
			} while (segments[idx] < end);
		}
		for (let i = 0; i < this._rangeCallbacks.length; i++) {
			const start = this._rangePoints[i * 2];
			const end = this._rangePoints[i * 2 + 1];
			let idx = rangeStartIndices[i];
			let sum = 0;
			let totalDuration = 0;
			let current = start;
			do {
				const p = parallelism[idx];
				idx++;
				const duration = segments[idx] - current;
				totalDuration += duration;
				current = segments[idx];
				sum += p * duration;
			} while (current < end);
			this._rangeCallbacks[i](sum / totalDuration);
		}
	}
}

module.exports = ParallelismFactorCalculator;
