/**
 * Cumulative prefix sum cache for O(log n) scroll-to-index lookups.
 *
 * Works for both row heights and column widths. Builds an array where
 * prefixSum[i] = sum of sizes for items 0..i-1.
 * prefixSum[0] = 0, prefixSum[n] = total size of all n items.
 *
 * Binary search on this array converts a pixel offset to an index in O(log n)
 * instead of O(n) linear iteration.
 *
 * @class PositionCache
 */ "use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "PositionCache", {
    enumerable: true,
    get: function() {
        return PositionCache;
    }
});
function _check_private_redeclaration(obj, privateCollection) {
    if (privateCollection.has(obj)) {
        throw new TypeError("Cannot initialize the same private elements twice on an object");
    }
}
function _class_apply_descriptor_get(receiver, descriptor) {
    if (descriptor.get) {
        return descriptor.get.call(receiver);
    }
    return descriptor.value;
}
function _class_apply_descriptor_set(receiver, descriptor, value) {
    if (descriptor.set) {
        descriptor.set.call(receiver, value);
    } else {
        if (!descriptor.writable) {
            throw new TypeError("attempted to set read only private field");
        }
        descriptor.value = value;
    }
}
function _class_extract_field_descriptor(receiver, privateMap, action) {
    if (!privateMap.has(receiver)) {
        throw new TypeError("attempted to " + action + " private field on non-instance");
    }
    return privateMap.get(receiver);
}
function _class_private_field_get(receiver, privateMap) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "get");
    return _class_apply_descriptor_get(receiver, descriptor);
}
function _class_private_field_init(obj, privateMap, value) {
    _check_private_redeclaration(obj, privateMap);
    privateMap.set(obj, value);
}
function _class_private_field_set(receiver, privateMap, value) {
    var descriptor = _class_extract_field_descriptor(receiver, privateMap, "set");
    _class_apply_descriptor_set(receiver, descriptor, value);
    return value;
}
var /**
   * A function that returns the total number of items (rows or columns).
   *
   * @type {Function}
   */ _totalItemsFn = /*#__PURE__*/ new WeakMap(), /**
   * A function that returns the size for a given index.
   *
   * @type {Function}
   */ _sizeFn = /*#__PURE__*/ new WeakMap(), /**
   * A function that returns the default size for items that return NaN/undefined.
   *
   * @type {Function}
   */ _defaultSizeFn = /*#__PURE__*/ new WeakMap();
class PositionCache {
    /**
   * Builds the prefix sum by reading the current total items, size function,
   * and default size from the configured providers.
   */ build() {
        const totalItems = _class_private_field_get(this, _totalItemsFn).call(this);
        const sizeFn = _class_private_field_get(this, _sizeFn);
        const defaultSize = _class_private_field_get(this, _defaultSizeFn).call(this);
        this.totalItems = totalItems;
        this.prefixSum = new Float64Array(totalItems + 1);
        this.prefixSum[0] = 0;
        for(let i = 0; i < totalItems; i++){
            const s = sizeFn(i);
            this.prefixSum[i + 1] = this.prefixSum[i] + (isNaN(s) ? defaultSize : s);
        }
    }
    /**
   * Returns the cumulative size at an index (sum of items 0..index-1).
   *
   * @param {number} index The item index.
   * @returns {number} The cumulative size before this index.
   */ getOffset(index) {
        if (!this.prefixSum || index <= 0) {
            return 0;
        }
        if (index >= this.totalItems) {
            return this.prefixSum[this.totalItems];
        }
        return this.prefixSum[index];
    }
    /**
   * Finds the item index at a given pixel offset using binary search.
   *
   * @param {number} offset The pixel offset.
   * @returns {number} The index whose cumulative start position is at or just before the offset.
   *   Returns `0` when there are no items (`totalItems === 0`), including when `offset > 0`.
   */ findIndexAtOffset(offset) {
        if (!this.isBuilt() || offset <= 0 || this.totalItems === 0) {
            return 0;
        }
        let lo = 0;
        let hi = this.totalItems;
        while(lo < hi){
            // eslint-disable-next-line no-bitwise
            const mid = lo + hi >>> 1;
            if (this.prefixSum[mid + 1] <= offset) {
                lo = mid + 1;
            } else {
                hi = mid;
            }
        }
        return Math.min(lo, this.totalItems - 1);
    }
    /**
   * Returns the size of a single item at the given index.
   *
   * @param {number} index The item index.
   * @returns {number} The size of the item (difference between consecutive prefix sums).
   */ getSizeAt(index) {
        if (!this.prefixSum || index < 0 || index >= this.totalItems) {
            return 0;
        }
        return this.prefixSum[index + 1] - this.prefixSum[index];
    }
    /**
   * Builds the prefix sum only when the cache is not yet built or the item
   * count has changed.
   */ ensureBuilt() {
        if (!this.isBuilt() || this.totalItems !== _class_private_field_get(this, _totalItemsFn).call(this)) {
            this.build();
        }
    }
    /**
   * Returns the total size of all items.
   *
   * @returns {number}
   */ getTotalSize() {
        if (!this.prefixSum) {
            return 0;
        }
        return this.prefixSum[this.totalItems];
    }
    /**
   * Invalidates the cache so it will be rebuilt on the next
   * {@link PositionCache#ensureBuilt} call.
   */ invalidate() {
        this.prefixSum = null;
        this.totalItems = 0;
    }
    /**
   * Returns whether the cache has been built.
   *
   * @returns {boolean}
   */ isBuilt() {
        return this.prefixSum !== null;
    }
    /**
   * @param {object} config The configuration object.
   * @param {Function} config.totalItemsFn A function that returns the total number of items (rows or columns).
   * @param {Function} config.sizeFn A function that returns the size for a given index.
   * @param {Function} config.defaultSizeFn A function that returns the default size for items
   *   that return NaN/undefined.
   */ constructor({ totalItemsFn, sizeFn, defaultSizeFn }){
        _class_private_field_init(this, _totalItemsFn, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _sizeFn, {
            writable: true,
            value: void 0
        });
        _class_private_field_init(this, _defaultSizeFn, {
            writable: true,
            value: void 0
        });
        /**
   * The prefix sum array. Contains the cumulative size of all items up to the index.
   *
   * @type {Float64Array|null}
   */ this.prefixSum = null;
        /**
   * The total number of items (rows or columns).
   *
   * @type {number}
   */ this.totalItems = 0;
        _class_private_field_set(this, _totalItemsFn, totalItemsFn);
        _class_private_field_set(this, _sizeFn, sizeFn);
        _class_private_field_set(this, _defaultSizeFn, defaultSizeFn);
    }
}
