/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Utils
 */
import { ReadonlySortedArray, SortedArray } from "@itwin/core-bentley";
/** An immutable set of [[TileUser]]s wherein uniqueness is determined by each TileUser's unique integer identifier.
 * @see [[UniqueTileUserSets]].
 */
export class ReadonlyTileUserSet extends ReadonlySortedArray {
    constructor(user) {
        super((lhs, rhs) => lhs.tileUserId - rhs.tileUserId);
        if (undefined !== user)
            this._array.push(user);
    }
}
class TileUserSet extends ReadonlyTileUserSet {
    copyFrom(src) {
        this._array.length = src.length;
        let i = 0;
        for (const user of src)
            this._array[i++] = user;
    }
    clone() {
        const clone = new TileUserSet();
        clone.copyFrom(this);
        return clone;
    }
    insert(user) { return this._insert(user); }
    remove(user) { return this._remove(user); }
    clear() { this._clear(); }
}
function compareTileUserSets(lhs, rhs) {
    if (lhs === rhs)
        return 0;
    let diff = lhs.length - rhs.length;
    if (0 === diff) {
        for (let i = 0; i < lhs.length; i++) {
            // lhs and rhs are the same length, and i is guaranteed to be in bounds for both.
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            diff = lhs.get(i).tileUserId - rhs.get(i).tileUserId;
            if (0 !== diff)
                break;
        }
    }
    return diff;
}
const emptySet = new ReadonlyTileUserSet();
const scratchSet = new TileUserSet();
class TileUserSetSet extends SortedArray {
    constructor() {
        super((lhs, rhs) => compareTileUserSets(lhs, rhs));
    }
    getForUser(user) {
        for (let i = 0; i < this.length; i++) {
            const set = this._array[i];
            // If 1 === set.length, set.get(0) is guaranteed to be defined.
            // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
            if (1 === set.length && set.get(0) === user)
                return set;
        }
        const newSet = new TileUserSet(user);
        this.insert(newSet);
        return newSet;
    }
    getTileUserSet(user, users) {
        if (undefined === users || users.isEmpty)
            return this.getForUser(user);
        // Use the scratch set for equality comparison - only allocate if no equivalent set already exists.
        const toFind = scratchSet;
        toFind.copyFrom(users);
        toFind.insert(user);
        const found = this.findEqual(toFind);
        if (undefined !== found) {
            toFind.clear();
            return found;
        }
        const newSet = toFind.clone();
        toFind.clear();
        this.insert(newSet);
        return newSet;
    }
    clearAll() {
        this.forEach((set) => set.clear());
        this.clear();
    }
    forgetUser(user) {
        for (const set of this)
            set.remove(user);
    }
}
/** Maintains a set of [[TileUserSets]] designed to minimize duplication.
 * For example, the tile request scheduler needs to associate with each [[TileRequest]] the set of [[TileUser]]s awaiting the request's response.
 * Using UniqueTileUserSets ensures all tiles requested by the same user use the same TileUserSet object.
 * Additionally, whenever a user is unregistered there is no need to track down every associated tile request - the user can just be removed from the TileUserSets managed by this object.
 * The TileUserSets managed by this object should be treated as immutable - "adding" a user to an existing set should be done using [[getTileUserSet]].
 */
export class UniqueTileUserSets {
    _sets = new TileUserSetSet();
    getTileUserSet(user, users) {
        return this._sets.getTileUserSet(user, users);
    }
    clearAll() {
        this._sets.clearAll();
    }
    clear() {
        this._sets.clear();
    }
    remove(user) {
        this._sets.forgetUser(user);
    }
    static get emptySet() {
        return emptySet;
    }
}
//# sourceMappingURL=TileUserSet.js.map