/*---------------------------------------------------------------------------------------------
 * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
 * See LICENSE.md in the project root for license terms and full copyright notice.
 *--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module AccuDraw
 */
import { BeUiEvent } from "@itwin/core-bentley";
import { AccuDraw, CompassMode, IModelApp, ItemField, NotifyMessageDetails, OutputMessagePriority, QuantityType, RotationMode, } from "@itwin/core-frontend";
import { ConditionalBooleanValue } from "@itwin/appui-abstract";
import { UiStateStorageStatus } from "@itwin/core-react";
import { UiFramework } from "../UiFramework";
import { SyncUiEventDispatcher, SyncUiEventId, } from "../syncui/SyncUiEventDispatcher";
// cspell:ignore dont
const compassModeToKeyMap = new Map([
    [CompassMode.Polar, "polar"],
    [CompassMode.Rectangular, "rectangular"],
]);
const rotationModeToKeyMap = new Map([
    [RotationMode.Top, "top"],
    [RotationMode.Front, "front"],
    [RotationMode.Side, "side"],
    [RotationMode.View, "view"],
    [RotationMode.ACS, "ACS"],
    [RotationMode.Context, "context"],
]);
/** AccuDraw Set Field Focus event
 * @beta */
export class AccuDrawSetFieldFocusEvent extends BeUiEvent {
}
/** AccuDraw Set Field Value to Ui event
 * @beta */
export class AccuDrawSetFieldValueToUiEvent extends BeUiEvent {
}
/** AccuDraw Set Field Value from Ui event
 * @beta */
export class AccuDrawSetFieldValueFromUiEvent extends BeUiEvent {
}
/** AccuDraw Set Field Lock event
 * @beta */
export class AccuDrawSetFieldLockEvent extends BeUiEvent {
}
/** AccuDraw Set Compass Mode event
 * @beta */
export class AccuDrawSetCompassModeEvent extends BeUiEvent {
}
/** AccuDraw Grab Input Focus event
 * @beta */
export class AccuDrawGrabInputFocusEvent extends BeUiEvent {
}
/** AccuDraw Ui Settings Changed event
 * @beta */
export class AccuDrawUiSettingsChangedEvent extends BeUiEvent {
}
/** Subclass of `AccuDraw` in `@itwin/core-frontend` to be used to initialize `IModelApp`.
 *
 * This implementation will generate the required events for the AppUI provided UI to update correctly.
 *
 * ```ts
 * await IModelApp.startup({
 *   accuDraw: new FrameworkAccuDraw()
 * });
 * ```
 * @beta
 */
class FrameworkAccuDraw extends AccuDraw {
    /** Determines if notifications should be displayed for AccuDraw changes */
    static get displayNotifications() {
        return FrameworkAccuDraw._displayNotifications;
    }
    static set displayNotifications(v) {
        FrameworkAccuDraw._displayNotifications = v;
        void UiFramework.getUiStateStorage().saveSetting(this._settingsNamespace, this._notificationsKey, v);
    }
    /* @internal */
    async loadUserSettings(storage) {
        const result = await storage.getSetting(FrameworkAccuDraw._settingsNamespace, FrameworkAccuDraw._notificationsKey);
        if (result.status === UiStateStorageStatus.Success)
            FrameworkAccuDraw._displayNotifications = result.setting;
    }
    /** AccuDraw User Interface settings */
    static get uiStateStorage() {
        return FrameworkAccuDraw._uiSettings;
    }
    static set uiStateStorage(v) {
        FrameworkAccuDraw._uiSettings = v;
        FrameworkAccuDraw.onAccuDrawUiSettingsChangedEvent.emit({});
    }
    constructor() {
        super();
        this.providerId = "FrameworkAccuDraw";
        this.handleSetFieldValueFromUiEvent = async (args) => {
            return this.processFieldInput(args.field, args.stringValue, false);
        };
        FrameworkAccuDraw.onAccuDrawSetFieldValueFromUiEvent.addListener(this.handleSetFieldValueFromUiEvent);
        UiFramework.registerUserSettingsProvider(this);
    }
    /** @internal */
    onCompassModeChange() {
        FrameworkAccuDraw.onAccuDrawSetCompassModeEvent.emit({
            mode: this.compassMode,
        });
        SyncUiEventDispatcher.dispatchSyncUiEvent(SyncUiEventId.AccuDrawCompassModeChanged);
        this.outputCompassModeMessage();
    }
    /** @internal */
    onRotationModeChange() {
        SyncUiEventDispatcher.dispatchSyncUiEvent(SyncUiEventId.AccuDrawRotationChanged);
        this.outputRotationMessage();
    }
    /** @internal */
    onFieldLockChange(index) {
        FrameworkAccuDraw.onAccuDrawSetFieldLockEvent.emit({
            field: index,
            lock: this.getFieldLock(index),
        });
    }
    /** @internal */
    onFieldValueChange(index) {
        const value = this.getValueByIndex(index);
        const formattedValue = FrameworkAccuDraw.getFieldDisplayValue(index);
        FrameworkAccuDraw.onAccuDrawSetFieldValueToUiEvent.emit({
            field: index,
            value,
            formattedValue,
        });
    }
    fieldValuesChanged() {
        this.onFieldValueChange(ItemField.X_Item);
        this.onFieldValueChange(ItemField.Y_Item);
        this.onFieldValueChange(ItemField.Z_Item);
        this.onFieldValueChange(ItemField.ANGLE_Item);
        this.onFieldValueChange(ItemField.DIST_Item);
    }
    /** @internal */
    setFocusItem(index) {
        FrameworkAccuDraw.onAccuDrawSetFieldFocusEvent.emit({ field: index });
    }
    /** Implemented by sub-classes to update ui fields to show current deltas or coordinates when inactive.
     * Should also choose active x or y input field in rectangular mode based on cursor position when
     * axis isn't locked to support "smart lock".
     * @internal
     */
    onMotion(_ev) {
        if (!this.isEnabled || this.isDeactivated || UiFramework.isContextMenuOpen)
            return;
        this.fieldValuesChanged();
        if (!this.dontMoveFocus)
            this.setFocusItem(this.newFocus);
    }
    /** Determine if the AccuDraw UI has focus
     * @internal
     */
    get hasInputFocus() {
        let hasFocus = false;
        const el = document.querySelector("div.uifw-accudraw-field-container");
        if (el)
            hasFocus = el.contains(document.activeElement);
        return hasFocus;
    }
    /** Implement this method to set focus to the AccuDraw UI.
     * @internal
     */
    grabInputFocus() {
        FrameworkAccuDraw.onAccuDrawGrabInputFocusEvent.emit({});
    }
    /** Gets the display value for an AccuDraw field */
    static getFieldDisplayValue(index) {
        const value = IModelApp.accuDraw.getValueByIndex(index);
        let formattedValue = value.toString();
        const formatterSpec = IModelApp.quantityFormatter.findFormatterSpecByQuantityType(ItemField.ANGLE_Item === index
            ? QuantityType.Angle
            : QuantityType.Length);
        // istanbul ignore else
        if (formatterSpec)
            formattedValue = IModelApp.quantityFormatter.formatQuantity(value, formatterSpec);
        return formattedValue;
    }
    /** AccuDraw Set Field Value from Ui. */
    static setFieldValueFromUi(field, stringValue) {
        FrameworkAccuDraw.onAccuDrawSetFieldValueFromUiEvent.emit({
            field,
            stringValue,
        });
    }
    outputInfoMessage(message) {
        // istanbul ignore else
        if (FrameworkAccuDraw.displayNotifications)
            IModelApp.notifications.outputMessage(new NotifyMessageDetails(OutputMessagePriority.Info, message));
    }
    outputCompassModeMessage() {
        if (FrameworkAccuDraw.displayNotifications) {
            let modeKey = compassModeToKeyMap.get(this.compassMode);
            // istanbul ignore if
            if (modeKey === undefined)
                modeKey = "polar";
            const modeString = UiFramework.translate(`accuDraw.compassMode.${modeKey}`);
            const modeMessage = UiFramework.localization.getLocalizedString("accuDraw.compassModeSet", { modeString, ns: UiFramework.localizationNamespace });
            this.outputInfoMessage(modeMessage);
        }
    }
    outputRotationMessage() {
        if (FrameworkAccuDraw.displayNotifications) {
            let rotationKey = rotationModeToKeyMap.get(this.rotationMode);
            // istanbul ignore if
            if (rotationKey === undefined)
                rotationKey = "top";
            const rotationString = UiFramework.translate(`accuDraw.rotation.${rotationKey}`);
            const rotationMessage = UiFramework.localization.getLocalizedString("accuDraw.rotationSet", { rotationString, ns: UiFramework.localizationNamespace });
            this.outputInfoMessage(rotationMessage);
        }
    }
}
FrameworkAccuDraw._displayNotifications = false;
FrameworkAccuDraw._settingsNamespace = "AppUiSettings";
FrameworkAccuDraw._notificationsKey = "AccuDrawNotifications";
/** AccuDraw Set Field Focus event. */
FrameworkAccuDraw.onAccuDrawSetFieldFocusEvent = new AccuDrawSetFieldFocusEvent();
/** AccuDraw Set Field Value to Ui event. */
FrameworkAccuDraw.onAccuDrawSetFieldValueToUiEvent = new AccuDrawSetFieldValueToUiEvent();
/** AccuDraw Set Field Value from Ui event. */
FrameworkAccuDraw.onAccuDrawSetFieldValueFromUiEvent = new AccuDrawSetFieldValueFromUiEvent();
/** AccuDraw Set Field Lock event. */
FrameworkAccuDraw.onAccuDrawSetFieldLockEvent = new AccuDrawSetFieldLockEvent();
/** AccuDraw Set Mode event. */
FrameworkAccuDraw.onAccuDrawSetCompassModeEvent = new AccuDrawSetCompassModeEvent();
/** AccuDraw Grab Input Focus event. */
FrameworkAccuDraw.onAccuDrawGrabInputFocusEvent = new AccuDrawGrabInputFocusEvent();
/** Determines if AccuDraw.rotationMode === RotationMode.Top */
FrameworkAccuDraw.isTopRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.Top, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.rotationMode === RotationMode.Front */
FrameworkAccuDraw.isFrontRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.Front, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.rotationMode === RotationMode.Side */
FrameworkAccuDraw.isSideRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.Side, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.rotationMode === RotationMode.View */
FrameworkAccuDraw.isViewRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.View, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.rotationMode === RotationMode.ACS */
FrameworkAccuDraw.isACSRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.ACS, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.rotationMode === RotationMode.Context */
FrameworkAccuDraw.isContextRotationConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.rotationMode === RotationMode.Context, [SyncUiEventId.AccuDrawRotationChanged]);
/** Determines if AccuDraw.compassMode === CompassMode.Polar */
FrameworkAccuDraw.isPolarModeConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.compassMode === CompassMode.Polar, [SyncUiEventId.AccuDrawCompassModeChanged]);
/** Determines if AccuDraw.compassMode === CompassMode.Rectangular */
FrameworkAccuDraw.isRectangularModeConditional = new ConditionalBooleanValue(() => IModelApp.accuDraw.compassMode === CompassMode.Rectangular, [SyncUiEventId.AccuDrawCompassModeChanged]);
/** AccuDraw Grab Input Focus event. */
FrameworkAccuDraw.onAccuDrawUiSettingsChangedEvent = new AccuDrawUiSettingsChangedEvent();
export { FrameworkAccuDraw };
//# sourceMappingURL=FrameworkAccuDraw.js.map