"use strict";
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Views
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.PerModelCategoryVisibility = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
/** Per-model category visibility permits the visibility of categories within a [[Viewport]] displaying a [[SpatialViewState]] to be overridden in
 * the context of individual [[GeometricModelState]]s.
 * If a category's visibility is overridden for a given model, then elements belonging to that category within that model will be displayed or hidden regardless of the category's inclusion in the Viewport's [[CategorySelectorState]].
 * The override affects geometry on all subcategories belonging to the overridden category. That is, if the category is overridden to be visible, then geometry on all subcategories of the category
 * will be visible, regardless of any [SubCategoryOverride]($common)s applied by the view's [[DisplayStyleState]].
 * @see [[Viewport.perModelCategoryVisibility]] to define the per-model category visibility for a viewport.
 * @public
 * @extensions
 */
var PerModelCategoryVisibility;
(function (PerModelCategoryVisibility) {
    /** Describes whether and how a category's visibility is overridden. */
    let Override;
    (function (Override) {
        /** The category's visibility is not overridden; its visibility is wholly controlled by the [[Viewport]]'s [[CategorySelectorState]]. */
        Override[Override["None"] = 0] = "None";
        /** The category is overridden to be visible. */
        Override[Override["Show"] = 1] = "Show";
        /** The category is overridden to be invisible. */
        Override[Override["Hide"] = 2] = "Hide";
    })(Override = PerModelCategoryVisibility.Override || (PerModelCategoryVisibility.Override = {}));
    function createOverrides(viewport) {
        return new PerModelCategoryVisibilityOverrides(viewport);
    }
    PerModelCategoryVisibility.createOverrides = createOverrides;
})(PerModelCategoryVisibility || (exports.PerModelCategoryVisibility = PerModelCategoryVisibility = {}));
class PerModelCategoryVisibilityOverride {
    constructor(modelId, categoryId, visible) {
        this.modelId = modelId;
        this.categoryId = categoryId;
        this.visible = visible;
    }
    reset(modelId, categoryId, visible) {
        this.modelId = modelId;
        this.categoryId = categoryId;
        this.visible = visible;
    }
}
function compareCategoryOverrides(lhs, rhs) {
    const cmp = (0, core_bentley_1.compareStrings)(lhs.modelId, rhs.modelId);
    return 0 === cmp ? (0, core_bentley_1.compareStrings)(lhs.categoryId, rhs.categoryId) : cmp;
}
/** The Viewport-specific implementation of PerModelCategoryVisibility.Overrides. */
class PerModelCategoryVisibilityOverrides extends core_bentley_1.SortedArray {
    constructor(vp) {
        super(compareCategoryOverrides);
        this._scratch = new PerModelCategoryVisibilityOverride("0", "0", false);
        this._vp = vp;
    }
    getOverride(modelId, categoryId) {
        this._scratch.reset(modelId, categoryId, false);
        const ovr = this.findEqual(this._scratch);
        if (undefined !== ovr)
            return ovr.visible ? PerModelCategoryVisibility.Override.Show : PerModelCategoryVisibility.Override.Hide;
        else
            return PerModelCategoryVisibility.Override.None;
    }
    /**
     * set the overrides for multiple perModelCategoryVisibility props, loading categoryIds from the iModel if necessary.
     * @see [[PerModelCategoryVisibility]]
     * @param perModelCategoryVisibility array of model category visibility overrides @see [[PerModelCategoryVisibility.Props]]
     * @param iModel Optional param iModel. If no iModel is provided, then the iModel associated with the viewport (used to construct this class) is used.
     * This optional iModel param is useful for apps which may show multiple iModels at once. Passing in an iModel ensures that the subcategories cache for the provided iModel
     * is populated as opposed to the iModel associated with the viewport which may or may not be an empty iModel.
     * @returns a promise that resolves once the overrides have been applied.
     */
    async setOverrides(perModelCategoryVisibility, iModel) {
        let anyChanged = false;
        const catIdsToLoad = [];
        const iModelToUse = iModel ? iModel : this._vp.iModel;
        for (const override of perModelCategoryVisibility) {
            const modelId = override.modelId;
            // The caller may pass a single categoryId as a string, if we don't convert this to an array we will iterate
            // over each individual character of that string, which is not the desired behavior.
            const categoryIds = typeof override.categoryIds === "string" ? [override.categoryIds] : override.categoryIds;
            const visOverride = override.visOverride;
            for (const categoryId of categoryIds) {
                if (this.findAndUpdateOverrideInArray(modelId, categoryId, visOverride)) {
                    anyChanged = true;
                    if (PerModelCategoryVisibility.Override.None !== visOverride) {
                        catIdsToLoad.push(categoryId);
                    }
                }
            }
        }
        if (anyChanged) {
            this._vp.setViewedCategoriesPerModelChanged();
            if (catIdsToLoad.length !== 0) {
                this._vp.subcategories.push(iModelToUse.subcategories, catIdsToLoad, () => this._vp.setViewedCategoriesPerModelChanged());
            }
        }
        return;
    }
    /** Find and update the override in the array of overrides. If override not found, adds it to the array.
     *  If the array was changed, returns true. */
    findAndUpdateOverrideInArray(modelId, categoryId, override) {
        const ovr = this._scratch;
        ovr.reset(modelId, categoryId, false);
        let changed = false;
        const index = this.indexOf(ovr);
        if (-1 === index) {
            if (PerModelCategoryVisibility.Override.None !== override) {
                this.insert(new PerModelCategoryVisibilityOverride(modelId, categoryId, PerModelCategoryVisibility.Override.Show === override));
                changed = true;
            }
        }
        else {
            if (PerModelCategoryVisibility.Override.None === override) {
                this._array.splice(index, 1);
                changed = true;
            }
            else if (this._array[index].visible !== (PerModelCategoryVisibility.Override.Show === override)) {
                this._array[index].visible = (PerModelCategoryVisibility.Override.Show === override);
                changed = true;
            }
        }
        return changed;
    }
    setOverride(modelIds, categoryIds, override) {
        let changed = false;
        for (const modelId of core_bentley_1.Id64.iterable(modelIds)) {
            for (const categoryId of core_bentley_1.Id64.iterable(categoryIds)) {
                if (this.findAndUpdateOverrideInArray(modelId, categoryId, override))
                    changed = true;
            }
        }
        if (changed) {
            this._vp.setViewedCategoriesPerModelChanged();
            if (PerModelCategoryVisibility.Override.None !== override) {
                // Ensure subcategories loaded.
                this._vp.subcategories.push(this._vp.iModel.subcategories, categoryIds, () => this._vp.setViewedCategoriesPerModelChanged());
            }
        }
    }
    clearOverrides(modelIds) {
        if (undefined === modelIds) {
            if (0 < this.length) {
                this.clear();
                this._vp.setViewedCategoriesPerModelChanged();
            }
            return;
        }
        for (let i = 0; i < this.length;) {
            const ovr = this._array[i];
            let removed = false;
            for (const modelId of core_bentley_1.Id64.iterable(modelIds)) {
                if (modelId === ovr.modelId) {
                    this._array.splice(i, 1);
                    this._vp.setViewedCategoriesPerModelChanged();
                    removed = true;
                    break;
                }
            }
            if (!removed)
                ++i;
        }
    }
    addOverrides(fs, ovrs) {
        const cache = this._vp.iModel.subcategories;
        for (const ovr of this._array) {
            const subcats = cache.getSubCategories(ovr.categoryId);
            if (undefined === subcats)
                continue;
            // It's pointless to override for models which aren't displayed...except if we do this, and then someone enables that model,
            // we would need to regenerate our symbology overrides in response. Preferably people wouldn't bother overriding models that
            // they don't want us to draw...
            /* if (!this._vp.view.viewsModel(ovr.modelId))
              continue; */
            // ###TODO: Avoid recomputing upper and lower portions of modelId if modelId repeated.
            // (Array is sorted first by modelId).
            // Also avoid computing if no effective overrides.
            const modelLo = core_bentley_1.Id64.getLowerUint32(ovr.modelId);
            const modelHi = core_bentley_1.Id64.getUpperUint32(ovr.modelId);
            for (const subcat of subcats) {
                const subcatLo = core_bentley_1.Id64.getLowerUint32(subcat);
                const subcatHi = core_bentley_1.Id64.getUpperUint32(subcat);
                const vis = fs.isSubCategoryVisible(subcatLo, subcatHi);
                if (vis !== ovr.visible) {
                    // Only care if visibility differs from that defined for entire view
                    let entry = ovrs.get(modelLo, modelHi);
                    if (undefined === entry) {
                        entry = new core_bentley_1.Id64.Uint32Set();
                        ovrs.set(modelLo, modelHi, entry);
                    }
                    entry.add(subcatLo, subcatHi);
                }
            }
        }
    }
}
//# sourceMappingURL=PerModelCategoryVisibility.js.map