/*---------------------------------------------------------------------------------------------
* 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 { BeEvent, Logger } from "@itwin/core-bentley";
import { IModelVersion, InternetConnectivityStatus, nativeAppIpcStrings, OverriddenBy, SyncMode, } from "@itwin/core-common";
import { FrontendLoggerCategory } from "./common/FrontendLoggerCategory";
import { IpcApp, NotificationHandler } from "./IpcApp";
import { NativeAppLogger } from "./NativeAppLogger";
/** NativeApp notifications from backend */
class NativeAppNotifyHandler extends NotificationHandler {
    get channelName() { return nativeAppIpcStrings.notifyChannel; }
    notifyInternetConnectivityChanged(status) {
        Logger.logInfo(FrontendLoggerCategory.NativeApp, "Internet connectivity changed");
        NativeApp.onInternetConnectivityChanged.raiseEvent(status);
    }
}
/**
 * The frontend of a native application
 * @see [Native Applications]($docs/learning/NativeApps.md)
 * @public
 */
export class NativeApp {
    static _removeAppNotify;
    /** A Proxy to call one of the [NativeAppFunctions]($common) functions via IPC. */
    static nativeAppIpc = IpcApp.makeIpcProxy(nativeAppIpcStrings.channelName);
    static catalogIpc = IpcApp.makeIpcProxy("catalogIModel/ipc");
    static _storages = new Map();
    static _onOnline = async () => {
        await NativeApp.setConnectivity(OverriddenBy.Browser, InternetConnectivityStatus.Online);
    };
    static _onOffline = async () => {
        await NativeApp.setConnectivity(OverriddenBy.Browser, InternetConnectivityStatus.Offline);
    };
    static async setConnectivity(by, status) {
        await this.nativeAppIpc.overrideInternetConnectivity(by, status);
    }
    static hookBrowserConnectivityEvents() {
        if (typeof window === "object" && window.ononline && window.onoffline) {
            window.addEventListener("online", this._onOnline);
            window.addEventListener("offline", this._onOffline);
        }
    }
    static unhookBrowserConnectivityEvents() {
        if (typeof window === "object" && window.ononline && window.onoffline) {
            window.removeEventListener("online", this._onOnline);
            window.removeEventListener("offline", this._onOffline);
        }
    }
    /** event called when internet connectivity changes, if known */
    static onInternetConnectivityChanged = new BeEvent();
    /** determine whether the app currently has internet connectivity, if known */
    static async checkInternetConnectivity() {
        return this.nativeAppIpc.checkInternetConnectivity();
    }
    /** @internal */
    static async overrideInternetConnectivity(status) {
        return this.nativeAppIpc.overrideInternetConnectivity(OverriddenBy.User, status);
    }
    static _isValid = false;
    static get isValid() { return this._isValid; }
    /**
     * This is called by either ElectronApp.startup or MobileApp.startup - it should not be called directly
     * @internal
     */
    static async startup(ipc, opts) {
        await IpcApp.startup(ipc, opts);
        if (this._isValid)
            return;
        this._isValid = true;
        this._removeAppNotify = NativeAppNotifyHandler.register(); // receives notifications from backend
        NativeApp.hookBrowserConnectivityEvents();
        // initialize current online state.
        if (window.navigator.onLine) {
            await this.setConnectivity(OverriddenBy.Browser, window.navigator.onLine ? InternetConnectivityStatus.Online : InternetConnectivityStatus.Offline);
        }
    }
    /** @internal */
    static async shutdown() {
        this._removeAppNotify?.();
        NativeApp.unhookBrowserConnectivityEvents();
        await NativeAppLogger.flush();
        await IpcApp.shutdown();
        this._isValid = false;
    }
    static async requestDownloadBriefcase(iTwinId, iModelId, downloadOptions, asOf = IModelVersion.latest()) {
        const shouldReportProgress = !!downloadOptions.progressCallback;
        let stopProgressEvents = () => { };
        if (shouldReportProgress) {
            const handleProgress = (_evt, data) => {
                downloadOptions.progressCallback?.(data);
            };
            stopProgressEvents = IpcApp.addListener(`nativeApp.progress-${iModelId}`, handleProgress);
        }
        const briefcaseId = (undefined !== downloadOptions.briefcaseId) ? downloadOptions.briefcaseId :
            (downloadOptions.syncMode === SyncMode.PullOnly ? 0 : await this.nativeAppIpc.acquireNewBriefcaseId(iModelId));
        const fileName = downloadOptions.fileName ?? await this.getBriefcaseFileName({ briefcaseId, iModelId });
        const requestProps = { iModelId, briefcaseId, iTwinId, asOf: asOf.toJSON(), fileName };
        const doDownload = async () => {
            try {
                await this.nativeAppIpc.downloadBriefcase(requestProps, shouldReportProgress, downloadOptions.progressInterval);
            }
            finally {
                stopProgressEvents();
            }
        };
        const requestCancel = async () => {
            const status = await this.nativeAppIpc.requestCancelDownloadBriefcase(fileName);
            if (status)
                stopProgressEvents();
            return status;
        };
        return { briefcaseId, fileName, downloadPromise: doDownload(), requestCancel };
    }
    /** Get the full path filename for a briefcase within the briefcase cache */
    static async getBriefcaseFileName(props) {
        return this.nativeAppIpc.getBriefcaseFileName(props);
    }
    /** Delete an existing briefcase
     * @param fileName the briefcase fileName
     */
    static async deleteBriefcase(fileName) {
        await this.nativeAppIpc.deleteBriefcaseFiles(fileName);
    }
    /** Get a list of all briefcase files held in the local briefcase cache directory */
    static async getCachedBriefcases(iModelId) {
        return this.nativeAppIpc.getCachedBriefcases(iModelId);
    }
    /**
     * Open a [[Storage]]. Creates a new Storage with that name if it does not already exist.
     * @param name Should be a local filename without an extension.
     * @returns a Promise for the [[Storage]].
     */
    static async openStorage(name) {
        let storage = this._storages.get(name);
        if (!storage) {
            storage = new Storage(await this.nativeAppIpc.storageMgrOpen(name));
            this._storages.set(storage.id, storage);
        }
        return storage;
    }
    /**
     * Close a Storage and optionally delete it.
     * @param storage normally not call directly instead use Storage.close()
     * @param deleteStorage if true, delete the storage from disk after closing it.
     */
    static async closeStorage(storage, deleteStorage = false) {
        if (!this._storages.has(storage.id))
            throw new Error(`Storage [Id=${storage.id}] not open`);
        await this.nativeAppIpc.storageMgrClose(storage.id, deleteStorage);
        this._storages.delete(storage.id);
    }
    /** Get the list of existing Storages on the local disk. */
    static async getStorageNames() {
        return NativeApp.nativeAppIpc.storageMgrNames();
    }
}
/**
 *  A local disk-based cache for key value pairs for NativeApps.
 * @note This should be used only for local caching, since its not guaranteed to exist permanently.
 * @public
 */
export class Storage {
    id;
    constructor(id) {
        this.id = id;
    }
    /** get the type of a value for a key, or undefined if not present. */
    async getValueType(key) {
        return NativeApp.nativeAppIpc.storageGetValueType(this.id, key);
    }
    /** Get the value for a key */
    async getData(key) {
        return NativeApp.nativeAppIpc.storageGet(this.id, key);
    }
    /** Set value for a key */
    async setData(key, value) {
        return NativeApp.nativeAppIpc.storageSet(this.id, key, value);
    }
    /**
     * Return an array of all keys in this Storage.
     * @note This can be expensive, depending on the number of keys present.
     */
    async getKeys() {
        return NativeApp.nativeAppIpc.storageKeys(this.id);
    }
    /** Remove a key and its data. */
    async removeData(key) {
        return NativeApp.nativeAppIpc.storageRemove(this.id, key);
    }
    /** Remove all keys and their data. */
    async removeAll() {
        return NativeApp.nativeAppIpc.storageRemoveAll(this.id);
    }
}
//# sourceMappingURL=NativeApp.js.map