/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module Picker
 */
import * as React from "react";
import { UiEvent } from "@itwin/appui-abstract";
import { assert, BeUiEvent, Logger } from "@itwin/core-bentley";
import { FuzzySearch, IModelApp } from "@itwin/core-frontend";
import { UiFramework } from "../UiFramework.js";
import { ViewUtilities } from "../utils/ViewUtilities.js";
import { ListItemType, ListPicker } from "./ListPicker.js";
import debounce from "lodash/debounce.js";
import { SvgSavedView } from "../icons/SvgSavedView.js";
import { useReduxFrameworkState } from "../uistate/useReduxFrameworkState.js";
import { Icon } from "@itwin/core-react";
/** ViewSelector Changed Event class.
 * @beta
 * @deprecated in 4.13.0. This class should not be used by applications to instantiate objects.
 */
// eslint-disable-next-line @typescript-eslint/no-deprecated
export class ViewSelectorChangedEvent extends UiEvent {
}
/** View Selector React component
 * @beta
 */
export class ViewSelector extends React.Component {
    static _onViewSelectorShowUpdateEvent = new BeUiEvent();
    _removeShowUpdateListener;
    _isMounted = false;
    _searchInput = "";
    static defaultProps = {
        showSpatials: true,
        showDrawings: true,
        showSheets: true,
        showUnknown: true,
    };
    /** Gets the [[ViewSelectorChangedEvent]].
     * @deprecated in 4.16.0. Use {@link ViewSelectorProps.onViewSelected} prop instead.
     */
    static onViewSelectorChangedEvent = new ViewSelectorChangedEvent(); // eslint-disable-line @typescript-eslint/no-deprecated
    /** Updates the ViewSelector show settings.
     */
    static updateShowSettings(showSpatials, showDrawings, showSheets, showUnknown) {
        ViewSelector._onViewSelectorShowUpdateEvent.emit({
            showSpatials,
            showDrawings,
            showSheets,
            showUnknown,
        });
    }
    /** Creates a ViewSelector */
    constructor(props) {
        super(props);
        this.state = {
            items: new Array(),
            title: UiFramework.translate("viewTypes.views"),
            initialized: false,
            showSpatials: props.showSpatials,
            showDrawings: props.showDrawings,
            showSheets: props.showSheets,
            showUnknown: props.showUnknown,
            searchBox: props.searchBox ? props.searchBox : true,
            panelOnly: props.panelOnly ? props.panelOnly : false,
            expand: false,
        };
    }
    async componentDidMount() {
        this._isMounted = true;
        if (this.props.listenForShowUpdates)
            this._removeShowUpdateListener =
                ViewSelector._onViewSelectorShowUpdateEvent.addListener(this._handleViewSelectorShowUpdateEvent);
        await this.loadViews();
    }
    /** @internal */
    async componentDidUpdate(prevProps) {
        if (this.props.imodel !== prevProps.imodel) {
            await this.loadViews();
        }
    }
    componentWillUnmount() {
        this._isMounted = false;
        if (this._removeShowUpdateListener)
            this._removeShowUpdateListener();
    }
    _handleViewSelectorShowUpdateEvent = (args) => {
        if (!this._isMounted)
            return;
        this.setState(args, async () => this.loadViews());
    };
    setStateContainers(views3d, views2d, sheets, unknown, views3dFiltered, views2dFiltered, sheetsFiltered, unknownFiltered) {
        const views3dContainer = {
            key: "views3dContainer",
            name: UiFramework.translate("viewTypes.spatialViews"),
            enabled: false,
            type: ListItemType.Container,
            children: views3dFiltered ? views3dFiltered : views3d,
        };
        const views2dContainer = {
            key: "views2dContainer",
            name: UiFramework.translate("viewTypes.drawings"),
            enabled: false,
            type: ListItemType.Container,
            children: views2dFiltered ? views2dFiltered : views2d,
        };
        const sheetContainer = {
            key: "sheetContainer",
            name: UiFramework.translate("viewTypes.sheets"),
            enabled: false,
            type: ListItemType.Container,
            children: sheetsFiltered ? sheetsFiltered : sheets,
        };
        const containers = [views3dContainer, views2dContainer, sheetContainer];
        if (unknown && unknown.length > 0) {
            // This should never show, but just in case we missed a type of view state
            const unknownContainer = {
                key: "unknownContainer",
                name: UiFramework.translate("viewTypes.others"),
                enabled: false,
                type: ListItemType.Container,
                children: unknownFiltered ? unknownFiltered : unknown,
            };
            if (unknown.length !== 0)
                containers.push(unknownContainer);
        }
        if (!this._isMounted)
            return;
        this.setState({
            items: containers,
            title: UiFramework.translate("viewTypes.views"),
            initialized: true,
        });
    }
    /**
     * Query the views and set the initial state with the iModel's views.
     */
    async loadViews() {
        // Query views and add them to state
        const views3d = [];
        const views2d = [];
        const sheets = [];
        const unknown = [];
        let views3dFiltered;
        let views2dFiltered;
        let sheetsFiltered;
        let unknownFiltered;
        const fuzzy = new FuzzySearch();
        if (this.props.imodel && this.props.imodel.views.getViewList) {
            const query = { wantPrivate: false };
            const specs = await this.props.imodel.views.getViewList(query);
            specs.forEach((spec) => {
                const viewItem = {
                    key: spec.id,
                    name: spec.name,
                    enabled: spec.id === IModelApp.viewManager.selectedView?.view.id
                        ? true
                        : false,
                    type: ListItemType.Item,
                };
                const className = ViewUtilities.getBisBaseClass(spec.class);
                if (ViewUtilities.isSpatial(className) && this.state.showSpatials)
                    views3d.push(viewItem);
                else if (ViewUtilities.isDrawing(className) && this.state.showDrawings)
                    views2d.push(viewItem);
                else if (ViewUtilities.isSheet(className) && this.state.showSheets)
                    sheets.push(viewItem);
                else if (this.state.showUnknown)
                    unknown.push(viewItem);
            });
            if (this._searchInput.length > 0) {
                const views3dFuzzySearchResults = fuzzy.search(views3d, ["name"], this._searchInput).results;
                const views2dFuzzySearchResults = fuzzy.search(views2d, ["name"], this._searchInput).results;
                const sheetsFuzzySearchResults = fuzzy.search(sheets, ["name"], this._searchInput).results;
                const unknownFuzzySearchResults = fuzzy.search(unknown, ["name"], this._searchInput).results;
                views3dFiltered = [];
                views2dFiltered = [];
                sheetsFiltered = [];
                unknownFiltered = [];
                views3dFuzzySearchResults.forEach(function (result) {
                    views3dFiltered?.push(result.item);
                });
                views2dFuzzySearchResults.forEach(function (result) {
                    views2dFiltered?.push(result.item);
                });
                sheetsFuzzySearchResults.forEach(function (result) {
                    sheetsFiltered?.push(result.item);
                });
                unknownFuzzySearchResults.forEach(function (result) {
                    unknownFiltered?.push(result.item);
                });
            }
        }
        this.setStateContainers(views3d, views2d, sheets, unknown, views3dFiltered, views2dFiltered, sheetsFiltered, unknownFiltered);
    }
    /**
     * Update state of the entries in the widget.
     * @param viewId Identifier for the relevant view
     */
    async updateState(viewId) {
        // Wait for initialization finished
        if (!this.state.initialized)
            return;
        // Query views and add them to state
        const views3d = this.state.items[0].children;
        const views2d = this.state.items[1].children;
        const sheets = this.state.items[2].children;
        const unknown = this.state.items.length > 3 ? this.state.items[3].children : [];
        const updateChildren = (item) => {
            if (item.key === viewId)
                return { ...item, enabled: true };
            else
                return { ...item, enabled: false };
        };
        this.setStateContainers(views3d.map(updateChildren), views2d.map(updateChildren), sheets.map(updateChildren), unknown.map(updateChildren));
    }
    // enable/disable the models
    _setEnabled = async (item, _enabled) => {
        const activeContentControl = // eslint-disable-next-line @typescript-eslint/no-deprecated
         UiFramework.content.getActiveContentControl();
        if (!this.props.onViewSelected &&
            (!activeContentControl ||
                !activeContentControl.supportsViewSelectorChange)) {
            Logger.logError(UiFramework.loggerCategory("ViewSelector"), `No active ContentControl for ViewSelector change`);
            return;
        }
        // Enable the item temporarily to let user see that their click was registered
        // while we query for view state and change the current view which may take a bit
        if (_enabled && item.type !== ListItemType.Container) {
            // This itemMapper simply looks through all the list items and their nested children and enables the one
            // that we have registered to enable
            // Also disable all other items
            const itemMapper = (tempItem) => {
                if (tempItem.type === ListItemType.Container) {
                    return { ...tempItem, children: tempItem.children.map(itemMapper) };
                }
                else if (tempItem.key === item.key) {
                    return { ...tempItem, enabled: true };
                }
                else {
                    return { ...tempItem, enabled: false };
                }
            };
            // Create the new array with the current item enabled
            const itemsWithEnabled = this.state.items.map(itemMapper);
            if (!this._isMounted)
                return;
            // Update the state so that we show the user it was enabled while we work in the background
            this.setState({ items: itemsWithEnabled });
        }
        assert(!!this.props.imodel);
        assert(!!item.name);
        // Load the view state using the viewSpec's ID
        const viewState = await this.props.imodel.views.load(item.key);
        if (this.props.onViewSelected) {
            this.props.onViewSelected({
                iModelConnection: this.props.imodel,
                viewDefinitionId: item.key,
                viewState,
                name: item.name,
            });
        }
        else if (activeContentControl) {
            // Let activeContentControl process the ViewSelector change
            await activeContentControl.processViewSelectorChange(this.props.imodel, item.key, viewState, item.name);
            // Emit a change event
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            ViewSelector.onViewSelectorChangedEvent.emit({
                iModelConnection: this.props.imodel,
                viewDefinitionId: item.key,
                viewState,
                name: item.name,
            });
        }
        // Set state to show enabled the view that got selected
        void this.updateState(item.key);
    };
    // Hook on the category selector being expanded so that we may initialize if needed
    _onExpanded = (expand) => {
        if (expand)
            void this.updateState(IModelApp.viewManager.selectedView
                ? IModelApp.viewManager.selectedView.view.id
                : undefined);
    };
    /**
     *  Renders ViewSelector component
     */
    render() {
        const { imodel, ...props } = this.props;
        return (React.createElement(ListPicker, { ...props, title: this.state.title, setEnabled: this._setEnabled, items: this.state.items, 
            // eslint-disable-next-line @typescript-eslint/no-deprecated
            icon: React.createElement(Icon, { iconSpec: React.createElement(SvgSavedView, null) }), onExpanded: this._onExpanded, searchBox: this.state.searchBox, onSearchValueChange: debounce((search) => {
                this._searchInput = search;
                if (this._isMounted &&
                    this.state.expand === false &&
                    this._searchInput.length > 0)
                    this.setState({ expand: true });
                void this.loadViews();
            }, 300), panelOnly: this.state.panelOnly, expanded: this.state.expand }));
    }
}
/** ViewSelector that is connected to the IModelConnection property in the Redux store. The application must set up the Redux store and include the FrameworkReducer.
 * @note Requires redux provider.
 * @beta
 * @deprecated in 4.15.0. Use {@link ViewSelector} instead.
 */
export function IModelConnectedViewSelector(props) {
    const iModel = useReduxFrameworkState(
    // eslint-disable-next-line @typescript-eslint/no-deprecated
    (state) => state?.sessionState.iModelConnection);
    return React.createElement(ViewSelector, { imodel: iModel, ...props });
}
//# sourceMappingURL=ViewSelector.js.map