/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
 * @module NativeApp
 */
import { join } from "path";
import { assert, BeEvent, ProcessDetector } from "@itwin/core-bentley";
import { InternetConnectivityStatus, nativeAppIpcStrings, } from "@itwin/core-common";
import { BriefcaseManager } from "./BriefcaseManager";
import { Downloads, ProgressStatus } from "./CheckpointManager";
import { IModelHost } from "./IModelHost";
import { IpcHandler, IpcHost, throttleProgressCallback } from "./IpcHost";
import { NativeAppStorage } from "./NativeAppStorage";
import { CatalogIModelHandler } from "./CatalogDb";
import { setOnlineStatus } from "./internal/OnlineStatus";
/**
 * Implementation of NativeAppFunctions
 */
class NativeAppHandler extends IpcHandler {
    get channelName() { return nativeAppIpcStrings.channelName; }
    async getAccessToken() {
        return IModelHost.authorizationClient?.getAccessToken();
    }
    async checkInternetConnectivity() {
        return NativeHost.checkInternetConnectivity();
    }
    async overrideInternetConnectivity(by, status) {
        NativeHost.overrideInternetConnectivity(by, status);
    }
    async acquireNewBriefcaseId(iModelId) {
        return BriefcaseManager.acquireNewBriefcaseId({ iModelId });
    }
    async getBriefcaseFileName(props) {
        return BriefcaseManager.getFileName(props);
    }
    async downloadBriefcase(request, reportProgress, progressInterval) {
        const args = {
            ...request,
            accessToken: await this.getAccessToken(),
            onProgress: (_a, _b) => checkAbort(),
        };
        const checkAbort = () => {
            assert(undefined !== args.fileName);
            const job = Downloads.isInProgress(args.fileName);
            return (job && job.request.abort === 1) ? ProgressStatus.Abort : ProgressStatus.Continue;
        };
        if (reportProgress) {
            const progressCallback = (loaded, total) => {
                IpcHost.send(`nativeApp.progress-${request.iModelId}`, { loaded, total });
                return checkAbort();
            };
            args.onProgress = throttleProgressCallback(progressCallback, checkAbort, progressInterval);
        }
        return BriefcaseManager.downloadBriefcase(args);
    }
    async requestCancelDownloadBriefcase(fileName) {
        const job = Downloads.isInProgress(fileName);
        if (job)
            job.request.abort = 1;
        return job !== undefined;
    }
    async deleteBriefcaseFiles(fileName) {
        await BriefcaseManager.deleteBriefcaseFiles(fileName, await IModelHost.getAccessToken());
    }
    async getCachedBriefcases(iModelId) {
        return BriefcaseManager.getCachedBriefcases(iModelId);
    }
    async storageMgrOpen(storageId) {
        return NativeAppStorage.open(storageId).id;
    }
    async storageMgrClose(storageId, deleteIt) {
        NativeAppStorage.find(storageId).close(deleteIt);
    }
    async storageMgrNames() {
        return NativeAppStorage.getStorageNames();
    }
    async storageGetValueType(storageId, key) {
        return NativeAppStorage.find(storageId).getValueType(key);
    }
    async storageGet(storageId, key) {
        return NativeAppStorage.find(storageId).getData(key);
    }
    async storageSet(storageId, key, value) {
        NativeAppStorage.find(storageId).setData(key, value);
    }
    async storageRemove(storageId, key) {
        NativeAppStorage.find(storageId).removeData(key);
    }
    async storageKeys(storageId) {
        return NativeAppStorage.find(storageId).getKeys();
    }
    async storageRemoveAll(storageId) {
        NativeAppStorage.find(storageId).removeAll();
    }
}
/**
 * Backend for desktop/mobile native applications
 * @public
 */
export class NativeHost {
    static _reachability;
    static _applicationName;
    constructor() { } // no instances - static methods only
    /** Event called when the internet connectivity changes, if known. */
    static onInternetConnectivityChanged = new BeEvent();
    static _appSettingsCacheDir;
    /** Get the local cache folder for application settings */
    static get appSettingsCacheDir() {
        return this._appSettingsCacheDir ??= join(IModelHost.cacheDir, "appSettings");
    }
    /** Send a notification to the NativeApp connected to this NativeHost. */
    static notifyNativeFrontend(methodName, ...args) {
        return IpcHost.send(nativeAppIpcStrings.notifyChannel, methodName, ...args);
    }
    static _isValid = false;
    static get isValid() { return this._isValid; }
    static get applicationName() { return this._applicationName; }
    /** Get the settings store for this NativeHost. */
    static get settingsStore() {
        return NativeAppStorage.open(this.applicationName);
    }
    /**
     * Start the backend of a native app.
     * @note this method calls [[IpcHost.startup]] internally.
     */
    static async startup(opt) {
        if (!this.isValid) {
            this._isValid = true;
            this.onInternetConnectivityChanged.addListener((status) => NativeHost.notifyNativeFrontend("notifyInternetConnectivityChanged", status));
            this._applicationName = opt?.nativeHost?.applicationName ?? "iTwinApp";
        }
        await IpcHost.startup(opt);
        if (IpcHost.isValid) { // for tests, we use NativeHost but don't have a frontend
            NativeAppHandler.register();
            CatalogIModelHandler.register();
        }
    }
    /** Shutdown native app backend. Also calls [[IpcHost.shutdown]] */
    static async shutdown() {
        this._isValid = false;
        this.onInternetConnectivityChanged.clear();
        await IpcHost.shutdown();
    }
    /** get current value of internet connectivity */
    static checkInternetConnectivity() {
        return this._reachability ?? InternetConnectivityStatus.Online;
    }
    /**
     * Override internet connectivity state
     * @param _overridenBy who overrode the value.
     * @internal
     */
    static overrideInternetConnectivity(_overridenBy, status) {
        if (this._reachability !== status) {
            this._reachability = status;
            this.onInternetConnectivityChanged.raiseEvent(status);
            if (ProcessDetector.isMobileAppBackend) {
                // Merely referencing NativeHost from a non-native backend causes a runtime exception (even
                // inside an if statement that checks that the backend is a mobile backend). This allows code
                // that needs to check connectivity to do so without referencing NativeHost directly.
                setOnlineStatus(status === InternetConnectivityStatus.Online);
            }
        }
    }
}
//# sourceMappingURL=NativeHost.js.map