/**
 * @license Copyright (c) 2017-2025, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */

'use strict';

/**
 * Collection of doclets as <String, Doclet[]> pairs. Also stores all doclets and their longnames as arrays.
 */
class DocletCollection {
	/**
	 * Creates collection of doclets.
	 */
	constructor() {
		this._data = {};
		this._allData = [];
	}

	get size() {
		return this._allData.length;
	}

	get isEmpty() {
		return this.size === 0;
	}

	/**
	 * Adds doclet to collection. Keeps it sorted by doclet's name.
	 *
	 * @param {String} category
	 * @param {Doclet} doclet
	 */
	add( category, doclet ) {
		if ( !this._data[ category ] ) {
			this._data[ category ] = [];
		}

		this._data[ category ] = insertSorted( doclet, this._data[ category ] );
		this._allData = insertSorted( doclet, this._allData );
	}

	/**
	 * Returns doclets filtered by category.
	 *
	 * @param {String} category
	 * @returns {Doclet[]}
	 */
	get( category ) {
		return this._data[ category ] || [];
	}

	/**
	 * Returns all doclets.
	 *
	 * @returns {Doclet[]}
	 */
	getAll() {
		return this._allData;
	}

	/**
	 * Returns first doclet.
	 *
	 * @returns {Doclet|null}
	 */
	getFirst() {
		return this.isEmpty ? null : this.getAll()[ 0 ];
	}

	getByLabel( label ) {
		return this.getAll().find( doclet => doclet.label === label );
	}
}

function insertSorted( doclet, array ) {
	array.splice( getInsertionIndex( doclet, array, sortByName ), 0, doclet );

	return array;
}

// Insertion sort.
function getInsertionIndex( doclet, array, compareFn, start = 0, end = array.length ) {
	if ( array.length === 0 ) {
		return 0;
	}

	const divisionIndex = parseInt( start + ( end - start ) / 2, 10 );
	const comparisonResult = compareFn( doclet, array[ divisionIndex ] );

	if ( comparisonResult === 0 ) {
		return divisionIndex + 1;
	}

	if ( end - start <= 1 ) {
		return comparisonResult == -1 ? divisionIndex : divisionIndex + 1;
	}

	if ( comparisonResult === -1 ) {
		return getInsertionIndex( doclet, array, compareFn, start, divisionIndex );
	} else if ( comparisonResult === 1 ) {
		return getInsertionIndex( doclet, array, compareFn, divisionIndex, end );
	}
}

function sortByName( a, b ) {
	if ( a.name < b.name ) {
		return -1;
	}

	if ( a.name > b.name ) {
		return 1;
	}

	return 0;
}

module.exports = DocletCollection;
