"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 Tools
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.HideIsolateEmphasizeManager = exports.HideIsolateEmphasizeActionHandler = exports.HideIsolateEmphasizeAction = void 0;
const core_bentley_1 = require("@itwin/core-bentley");
const core_common_1 = require("@itwin/core-common");
const core_frontend_1 = require("@itwin/core-frontend");
const SyncUiEventDispatcher_1 = require("../syncui/SyncUiEventDispatcher");
/** Supported Hide, Isolate, and Emphasize Actions. These also serve as FeatureTracking Ids.
 * @public
 */
var HideIsolateEmphasizeAction;
(function (HideIsolateEmphasizeAction) {
    HideIsolateEmphasizeAction["EmphasizeSelectedElements"] = "EmphasizeSelectedElements";
    HideIsolateEmphasizeAction["IsolateSelectedElements"] = "IsolateSelectedElements";
    HideIsolateEmphasizeAction["IsolateSelectedCategories"] = "IsolateSelectedCategories";
    HideIsolateEmphasizeAction["IsolateSelectedModels"] = "IsolateSelectedModels";
    HideIsolateEmphasizeAction["HideSelectedElements"] = "HideSelectedElements";
    HideIsolateEmphasizeAction["HideSelectedModels"] = "HideSelectedModels";
    HideIsolateEmphasizeAction["HideSelectedCategories"] = "HideSelectedCategories";
    HideIsolateEmphasizeAction["ClearHiddenIsolatedEmphasized"] = "ClearHiddenIsolatedEmphasized";
    HideIsolateEmphasizeAction["ClearOverrideModels"] = "ClearOverrideModels";
    HideIsolateEmphasizeAction["ClearOverrideCategories"] = "ClearOverrideCategories";
})(HideIsolateEmphasizeAction || (exports.HideIsolateEmphasizeAction = HideIsolateEmphasizeAction = {}));
/** Overrides given models to provide emphasize functionality
 * @public
 */
class ModelOverrideProvider {
    constructor(modelIds, defaultAppearance) {
        this.modelIds = modelIds;
        this.defaultAppearance = defaultAppearance;
    }
    addFeatureOverrides(overrides, _viewport) {
        overrides.setDefaultOverrides(this.defaultAppearance, true);
        // Override with nothing so that we keep the model looking normal and override the default appearance of everything else
        const appearance = core_common_1.FeatureAppearance.fromJSON({});
        this.modelIds.forEach((modelId) => {
            overrides.override({ modelId, appearance, onConflict: "replace" });
        });
    }
}
/** Overrides given categories to provide emphasize functionality
 *  @public
 */
class SubCategoryOverrideProvider {
    constructor(subCategoryIds, defaultAppearance) {
        this.subCategoryIds = subCategoryIds;
        this.defaultAppearance = defaultAppearance;
    }
    addFeatureOverrides(overrides, _viewport) {
        overrides.setDefaultOverrides(this.defaultAppearance, true);
        // Override with nothing so that we keep the category looking normal and override the default appearance of everything else
        const appearance = core_common_1.FeatureAppearance.fromJSON({});
        this.subCategoryIds.forEach((subCategoryId) => {
            overrides.override({ subCategoryId, appearance, onConflict: "replace" });
        });
    }
}
/**
 * Interface for class that handles Hide, Isolate, and Emphasize Actions
 * @public
 */
class HideIsolateEmphasizeActionHandler {
    /** String Id sent to allow UI to refresh its display state.  */
    static get hideIsolateEmphasizeUiSyncId() {
        return "selection-context-emphasize-elements-changed";
    }
}
exports.HideIsolateEmphasizeActionHandler = HideIsolateEmphasizeActionHandler;
HideIsolateEmphasizeActionHandler.emphasizeElementsChanged = new core_bentley_1.BeEvent();
/** Provides helper functions for doing commands on logical selection like categories and subjects.
 * @public
 */
class HideIsolateEmphasizeManager extends HideIsolateEmphasizeActionHandler {
    /** Returns true if there is only a category hilited. */
    static categorySelected(vp) {
        const subcategories = vp.iModel.hilited.subcategories;
        return subcategories.size === 1;
    }
    /** Returns true if only a model is hilited. */
    static modelSelected(vp) {
        const models = vp.iModel.hilited.models;
        return models.size === 1;
    }
    /**
     * Hide the hilited categories.
     * @param vp Viewport to affect
     */
    static hideSelectedCategory(vp) {
        const ids = vp.iModel.hilited.subcategories.toId64Set();
        vp.changeCategoryDisplay(ids, false);
    }
    /** Get sub categories that relate to the category Id */
    static async _getSubCategories(iModelConnection, categoryIds) {
        const allSubcats = [];
        const request = iModelConnection.subcategories.load(categoryIds);
        if (request)
            await request.promise;
        for (const categoryId of categoryIds) {
            const subcats = iModelConnection.subcategories.getSubCategories(categoryId);
            if (subcats)
                allSubcats.push(...subcats);
        }
        return allSubcats;
    }
    /**
     * Emphasize hilited categories.
     * @param vp Viewport to affect
     */
    static async emphasizeSelectedCategory(vp) {
        const ids = vp.iModel.hilited.subcategories.toId64Array();
        if (ids.length === 0)
            return;
        const defaultAppearance = core_frontend_1.EmphasizeElements.getOrCreate(vp).createDefaultAppearance();
        core_frontend_1.EmphasizeElements.clear(vp);
        const subcats = await HideIsolateEmphasizeManager._getSubCategories(vp.iModel, ids);
        vp.addFeatureOverrideProvider(new SubCategoryOverrideProvider(subcats, defaultAppearance));
    }
    /**
     * Hide the selected model
     * @param vp Viewport to affect
     */
    static hideSelectedModel(vp) {
        const ids = vp.iModel.hilited.models.toId64Array();
        if (ids.length === 0)
            return;
        vp.changeModelDisplay(ids, false);
    }
    /**
     * Isolate the selected model
     * @param vp Viewport to affect
     */
    static emphasizeSelectedModel(vp) {
        const ids = vp.iModel.hilited.models.toId64Array();
        if (ids.length === 0)
            return;
        const defaultAppearance = core_frontend_1.EmphasizeElements.getOrCreate(vp).createDefaultAppearance();
        core_frontend_1.EmphasizeElements.clear(vp);
        vp.addFeatureOverrideProvider(new ModelOverrideProvider(ids, defaultAppearance));
    }
    /**
     * Isolate the selected elements
     * @param vp Viewport to affect
     */
    static isolateSelected(vp) {
        core_frontend_1.EmphasizeElements.getOrCreate(vp).isolateSelectedElements(vp, true, false); // Isolate selected elements
    }
    /**
     * Hide the selected elements
     * @param vp Viewport to affect
     */
    static hideSelected(vp) {
        core_frontend_1.EmphasizeElements.getOrCreate(vp).hideSelectedElements(vp, false, false); // Hide all selected elements
    }
    /**
     * Clear Hidden,Isolated, or Emphasized elements in specified view
     * @param vp Viewport to affect
     *
     */
    static clearEmphasize(vp) {
        if (vp) {
            core_frontend_1.EmphasizeElements.getOrCreate(vp).clearEmphasizedElements(vp);
            core_frontend_1.EmphasizeElements.clear(vp);
        }
    }
    /**
     * Emphasize the selected elements from either presentation layer's logical selection or selected graphics
     * @param vp Viewport to affect
     * @param emphasisSilhouette defaults to true
     */
    static async emphasizeSelected(vp, emphasisSilhouette = true) {
        if (HideIsolateEmphasizeManager.categorySelected(vp)) {
            await HideIsolateEmphasizeManager.emphasizeSelectedCategory(vp);
            return;
        }
        else if (HideIsolateEmphasizeManager.modelSelected(vp)) {
            HideIsolateEmphasizeManager.emphasizeSelectedModel(vp);
            return;
        }
        const ee = core_frontend_1.EmphasizeElements.getOrCreate(vp);
        ee.wantEmphasis = emphasisSilhouette;
        ee.emphasizeSelectedElements(vp, undefined, true, false); // Emphasize elements by making all others grey/transparent
        vp.isFadeOutActive = true; // Enable flat alpha for greyed out elements…
    }
    /**
     * Isolate hilited models.
     * @param vp Viewport to affect
     */
    static async isolateSelectedModel(vp) {
        const ids = vp.iModel.hilited.models.toId64Array();
        if (ids.length === 0)
            return;
        await vp.replaceViewedModels(ids);
    }
    /**
     * Isolate hilited categories.
     * @param vp Viewport to affect
     */
    static isolateSelectedCategory(vp) {
        const ids = vp.iModel.hilited.subcategories.toId64Set();
        const categoriesToDrop = [];
        vp.view.categorySelector.categories.forEach((categoryId) => {
            if (!ids.has(categoryId))
                categoriesToDrop.push(categoryId);
        });
        vp.changeCategoryDisplay(categoriesToDrop, false);
        vp.changeCategoryDisplay(ids, true);
    }
    static async getSelectionSetElementProps(iModel) {
        const persistentElementIds = new Set();
        // Filter out transient elements because they do not exist in the iModel.
        iModel.selectionSet.elements.forEach((elementId) => {
            if (!core_bentley_1.Id64.isTransient(elementId))
                persistentElementIds.add(elementId);
        });
        return iModel.elements.getProps(persistentElementIds);
    }
    static async getSelectionSetElementModels(iModel) {
        const props = await HideIsolateEmphasizeManager.getSelectionSetElementProps(iModel);
        const modelIds = new Set();
        for (const prop of props)
            if (prop.model)
                modelIds.add(prop.model);
        return modelIds;
    }
    static async getSelectionSetElementCategories(iModel) {
        const props = (await HideIsolateEmphasizeManager.getSelectionSetElementProps(iModel));
        const categoryIds = new Set();
        for (const prop of props)
            if (prop.category)
                categoryIds.add(prop.category);
        return categoryIds;
    }
    /**
     * Isolate either based on Presentation selection, if defined, else the selected graphic elements
     * @param vp Viewport to affect
     */
    static async isolateCommand(vp) {
        if (HideIsolateEmphasizeManager.categorySelected(vp)) {
            HideIsolateEmphasizeManager.isolateSelectedCategory(vp);
            return;
        }
        else if (HideIsolateEmphasizeManager.modelSelected(vp)) {
            await HideIsolateEmphasizeManager.isolateSelectedModel(vp);
            return;
        }
    }
    /**
     * Isolate model from selected elements
     * @param vp Viewport to affect
     */
    static async isolateSelectedElementsModel(vp) {
        const modelsToKeep = new Set(await HideIsolateEmphasizeManager.getSelectionSetElementModels(vp.iModel));
        if (vp.view.isSpatialView()) {
            const modelsToTurnOff = [...vp.view.modelSelector.models].filter((modelId) => !modelsToKeep.has(modelId));
            this.updateModelOverride(vp, modelsToTurnOff);
        }
        await vp.replaceViewedModels(modelsToKeep);
    }
    /**
     * Isolate the selected category found in SelectionSet elements
     * @param vp Viewport to affect
     */
    static async isolateSelectedElementsCategory(vp) {
        const categoriesToKeep = new Set(await HideIsolateEmphasizeManager.getSelectionSetElementCategories(vp.iModel));
        const categoriesToTurnOff = [...vp.view.categorySelector.categories].filter((categoryId) => !categoriesToKeep.has(categoryId));
        vp.changeCategoryDisplay(categoriesToTurnOff, false);
        vp.changeCategoryDisplay(categoriesToKeep, true);
        this.updateCategoryOverride(vp, categoriesToTurnOff);
    }
    /**
     * Hide either based on Presentation selection, if defined, else the selected graphic elements
     * @param vp Viewport to affect
     */
    static async hideCommand(vp) {
        if (HideIsolateEmphasizeManager.categorySelected(vp)) {
            HideIsolateEmphasizeManager.hideSelectedCategory(vp);
            return;
        }
        else if (HideIsolateEmphasizeManager.modelSelected(vp)) {
            HideIsolateEmphasizeManager.hideSelectedModel(vp);
            return;
        }
        core_frontend_1.EmphasizeElements.getOrCreate(vp).hideSelectedElements(vp, false, false); // Hide selected elements
    }
    /**
     * Hide the models defined by the elements in the current SelectionSet
     * @param vp Viewport to affect
     */
    static async hideSelectedElementsModel(vp) {
        const modelIds = await HideIsolateEmphasizeManager.getSelectionSetElementModels(vp.iModel);
        vp.changeModelDisplay(modelIds, false);
        this.updateModelOverride(vp, [...modelIds]);
    }
    /**
     * Clear (restore) the previously hidden/isolated models hidden by hideSelectedElementsModel
     * @param vp Viewport to affect
     */
    static clearOverrideModels(vp) {
        const ids = this._overrideModelIds.get(vp);
        if (ids) {
            vp.changeModelDisplay([...ids], true);
            this.clearModelOverride(vp);
        }
    }
    /**
     * Determine if models are hidden by hideSelectedElementsModel or isolateSelectedElementsModel
     * @param vp Viewport to affect
     */
    static isOverrideModels(vp) {
        const ids = this._overrideModelIds.get(vp);
        return ids ? [...ids].length > 0 : false;
    }
    /**
     * Determine if categories are hidden by hideSelectedElementsCategory or isolateSelectedElementsCategory
     * @param vp Viewport to affect
     */
    static isOverrideCategories(vp) {
        const ids = this._overrideCategoryIds.get(vp);
        return ids ? [...ids].length > 0 : false;
    }
    /**
     * Hide the categories defined by the elements in the current SelectionSet
     * @param vp Viewport to affect
     */
    static async hideSelectedElementsCategory(vp) {
        const categoryIds = await HideIsolateEmphasizeManager.getSelectionSetElementCategories(vp.iModel);
        vp.changeCategoryDisplay(categoryIds, false);
        this.updateCategoryOverride(vp, [...categoryIds]);
    }
    /**
     * Clear (restore) the previously hidden categories hidden by hideSelectedElementsCategory
     * @param vp Viewport to affect
     */
    static clearOverrideCategories(vp) {
        const ids = this._overrideCategoryIds.get(vp);
        if (ids) {
            vp.changeCategoryDisplay([...ids], true);
            this.clearCategoryOverride(vp);
        }
    }
    /** Checks to see if any featureOverrideProviders are active */
    areFeatureOverridesActive(vp) {
        // Check all the emphasize possibilities
        const emphasizeElementsProvider = vp.findFeatureOverrideProviderOfType(core_frontend_1.EmphasizeElements);
        if (undefined !== emphasizeElementsProvider &&
            emphasizeElementsProvider.isActive(vp))
            return true;
        else if (vp.neverDrawn && vp.neverDrawn.size > 0)
            return true;
        const modelOverrideProvider = vp.findFeatureOverrideProviderOfType(ModelOverrideProvider);
        if (undefined !== modelOverrideProvider &&
            modelOverrideProvider.modelIds.length > 0)
            return true;
        const subCategoryOverrideProvider = vp.findFeatureOverrideProviderOfType(SubCategoryOverrideProvider);
        if (undefined !== subCategoryOverrideProvider &&
            subCategoryOverrideProvider.subCategoryIds.length > 0)
            return true;
        // Check hide/isolate possibilities
        if (HideIsolateEmphasizeManager.isOverrideCategories(vp))
            return true;
        if (HideIsolateEmphasizeManager.isOverrideModels(vp))
            return true;
        return false;
    }
    /**
     * Function that is run when `IsolateSelectedElementsModel` tool button is pressed
     */
    async processIsolateSelectedElementsModel() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        await HideIsolateEmphasizeManager.isolateSelectedElementsModel(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.IsolateSelectedModels,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `IsolateSelectedElementsCategory` tool button is pressed
     */
    async processIsolateSelectedElementsCategory() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        await HideIsolateEmphasizeManager.isolateSelectedElementsCategory(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.IsolateSelectedCategories,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `IsolateSelected` tool button is pressed
     */
    async processIsolateSelected(clearSelection = true) {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        HideIsolateEmphasizeManager.isolateSelected(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.IsolateSelectedElements,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
        if (!clearSelection)
            return;
        // clear out selection now that any callbacks have processed
        const selection = vp.view.iModel.selectionSet;
        if (selection.isActive)
            selection.emptyAll();
    }
    /**
     * Function that is run when `HideSelectedElementsModel` tool button is pressed
     */
    async processHideSelectedElementsModel() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        await HideIsolateEmphasizeManager.hideSelectedElementsModel(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.HideSelectedModels,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `HideSelectedElementsCategory` tool button is pressed
     */
    async processHideSelectedElementsCategory() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        await HideIsolateEmphasizeManager.hideSelectedElementsCategory(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.HideSelectedCategories,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `HideSelected` tool button is pressed
     */
    async processHideSelected() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        HideIsolateEmphasizeManager.hideSelected(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.HideSelectedElements,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
        // clear out selection now that any callbacks have processed
        const selection = vp.view.iModel.selectionSet;
        if (selection.isActive)
            selection.emptyAll();
    }
    /**
     * Function that is run when `EmphasizeSelected` tool button is pressed
     */
    async processEmphasizeSelected() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        await HideIsolateEmphasizeManager.emphasizeSelected(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.EmphasizeSelectedElements,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
        // clear out selection now that any callbacks have processed
        const selection = vp.view.iModel.selectionSet;
        if (selection.isActive)
            selection.emptyAll();
    }
    /**
     * Function that is run when `ClearEmphasize` tool button is pressed
     */
    async processClearEmphasize() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        HideIsolateEmphasizeManager.clearEmphasize(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.ClearHiddenIsolatedEmphasized,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `ClearOverrideModels` tool button is pressed
     */
    async processClearOverrideModels() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        HideIsolateEmphasizeManager.clearOverrideModels(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.ClearOverrideModels,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Function that is run when `ClearOverrideCategories` tool button is pressed
     */
    async processClearOverrideCategories() {
        const vp = core_frontend_1.IModelApp.viewManager.selectedView;
        if (!vp)
            return;
        HideIsolateEmphasizeManager.clearOverrideCategories(vp);
        HideIsolateEmphasizeActionHandler.emphasizeElementsChanged.raiseEvent({
            viewport: vp,
            action: HideIsolateEmphasizeAction.ClearOverrideCategories,
        });
        SyncUiEventDispatcher_1.SyncUiEventDispatcher.dispatchSyncUiEvent(HideIsolateEmphasizeActionHandler.hideIsolateEmphasizeUiSyncId);
    }
    /**
     * Add category ids to the category override cache (hidden or isolated categories)
     */
    static updateCategoryOverride(vp, ids) {
        const prevIds = this._overrideCategoryIds.get(vp);
        const newIds = [...(prevIds || []), ...ids];
        this._overrideCategoryIds.set(vp, new Set(newIds));
    }
    /**
     * Add model ids to the model override cache (hidden or isolated models)
     */
    static updateModelOverride(vp, ids) {
        const prevIds = this._overrideModelIds.get(vp);
        const newIds = [...(prevIds || []), ...ids];
        this._overrideModelIds.set(vp, new Set(newIds));
    }
    /**
     * Return the list of category overrides (hidden or isolated categories)
     */
    static getCategoryOverrides(vp) {
        return this._overrideCategoryIds.get(vp);
    }
    /**
     * Return the list of model overrides (hidden or isolated models)
     */
    static getModelOverrides(vp) {
        return this._overrideModelIds.get(vp);
    }
    /**
     * Clear the category override cache (hidden or isolated categories)
     */
    static clearCategoryOverride(vp) {
        this._overrideCategoryIds.delete(vp);
    }
    /**
     * Clear the model override cache (hidden or isolated models)
     */
    static clearModelOverride(vp) {
        this._overrideModelIds.delete(vp);
    }
}
exports.HideIsolateEmphasizeManager = HideIsolateEmphasizeManager;
HideIsolateEmphasizeManager._overrideCategoryIds = new Map();
HideIsolateEmphasizeManager._overrideModelIds = new Map();
//# sourceMappingURL=HideIsolateEmphasizeManager.js.map