import { DistributedStoreClient } from "../distributedStoreClient";
import { FinsembleWindow } from "../../common/FinsembleWindow";
import { Logger } from "../logger";
import { System } from "../../common/system";
import { getMyWindowIdentifier } from "../../common/util";
import { Globals } from "../../common/Globals";
import SystemManagerClient from "../../common/systemManagerClient";
import { series as asyncSeries } from "async";
export class DialogManagerClientController {
    constructor() {
        this.initialized = false;
        /**
         * Can be used to set a specific timeout
         */
        this.userInputTimeout = null;
        /**
         * A global store used to track which windows are registered as dialogs and their state.
         * Dialogs are put in the "available" pool when they are available for use. They are put
         * in the "open" pool when they are in use.
         */
        this.DialogStore = null;
        /**
         * Will be set to true if the current window is a dialog
         */
        this.isDialog = false;
        /**
         * Contains a unique channel name that a dialog can use to communicate back to
         * its originating window. The originating window creates the channel as
         * a query responder.
         */
        this.RESPONDER_CHANNEL = null;
        /**
         * Used to send a message back to whoever opened this dialog
         */
        this.openerMessage = null;
        /**
         * Same as FEAGlobals.finsembleWindow
         */
        this.finsembleWindow = null;
        /**
         * Will be set with the spawn data, which contains the input params and responseChannel
         */
        this.spawnData = {};
    }
    /**
     * Checks whether the window is a dialog, setting this.isDialog to true or false
     * @param {cb} cb The callback to be invoked after the method completes successfully.
     */
    async checkIfWindowIsDialog(cb) {
        var _a, _b, _c, _d, _e, _f;
        if (Globals.finsembleWindow) {
            const { err, data: options } = await Globals.finsembleWindow.getOptions();
            this.spawnData = (_b = (_a = options === null || options === void 0 ? void 0 : options.customData) === null || _a === void 0 ? void 0 : _a.spawnData) !== null && _b !== void 0 ? _b : {};
            this.isDialog = (_f = (_e = (_d = (_c = options === null || options === void 0 ? void 0 : options.customData) === null || _c === void 0 ? void 0 : _c.foreign) === null || _d === void 0 ? void 0 : _d.clients) === null || _e === void 0 ? void 0 : _e.dialogManager) === null || _f === void 0 ? void 0 : _f.isDialog;
            cb === null || cb === void 0 ? void 0 : cb(err);
        }
        else {
            this.isDialog = false;
            cb === null || cb === void 0 ? void 0 : cb("Freestanding apps cannot be dialogs");
        }
    }
    /**
     * Registers the window as a dialog with the global store. If the component is incapable of being used as a dialog (this is set in the component's config), the callback is immediately invoked.
     *
     * **Note:** This method checks whether the calling window is a dialog.
     * @param {function} callback The callback to be invoked after the method completes successfully.
     */
    async registerWithStore(cb) {
        var _a;
        if (this.isDialog) {
            const myWindowIdentifier = await getMyWindowIdentifier();
            this.RESPONDER_CHANNEL = `Dialog.${myWindowIdentifier.windowName}.Show`;
            await ((_a = this.DialogStore) === null || _a === void 0 ? void 0 : _a.set(["dialogs", "available", myWindowIdentifier.windowName], myWindowIdentifier));
        }
        cb === null || cb === void 0 ? void 0 : cb();
    }
    /**
     * Establishes a connection to the distributed store that is used
     * by all dialogs. This function may be called many times but will
     * only initialize one time.
     */
    initializeOnce() {
        return new Promise(async (resolve) => {
            // Only initialize once
            if (this.initialized) {
                resolve();
                return;
            }
            await SystemManagerClient.waitForStartup("windowService");
            await this.checkIfWindowIsDialog();
            asyncSeries([
                this.createStore.bind(this),
                this.registerWithStore.bind(this),
                this.getFinsembleWindow.bind(this),
                (done) => {
                    done();
                },
            ], () => {
                this.initialized = true;
                resolve();
            });
        });
    }
    /**
     * Creates the global store if it doesn't exist.
     * @param {function} callback
     */
    async createStore(cb) {
        const defaults = {
            dialogs: {
                opened: {},
                available: {},
            },
        };
        const { err, data } = await DistributedStoreClient.createStore({
            store: "DialogStore",
            values: defaults,
            global: true,
        });
        if (err || !data) {
            Logger.error(`DialogManagerClient could not create store "DialogStore"`, err);
        }
        else {
            this.DialogStore = data;
        }
        cb === null || cb === void 0 ? void 0 : cb();
    }
    /**
     */
    async getFinsembleWindow(cb) {
        // this.finsembleWindow must only be initialized in actual dialogs. Otherwise it causes side effects in services.
        if (this.isDialog) {
            const { wrap } = await FinsembleWindow.getInstance({ name: System.Window.getCurrent().name });
            this.finsembleWindow = wrap;
        }
        cb === null || cb === void 0 ? void 0 : cb();
    }
    async getAvailableDialog(type, cb) {
        if (!this.DialogStore) {
            const error = `DialogMangerClient.getAvailableDialogs() store not yet initialized}`;
            Logger.system.error(error);
            cb === null || cb === void 0 ? void 0 : cb(null);
            return null;
        }
        const { err, data: availableDialogs } = await this.DialogStore.getGlobal([
            "dialogs",
            "available",
        ]);
        if (err) {
            const error = `DialogMangerClient.getAvailableDialogs() store error ${err}`;
            Logger.system.error(error);
            cb === null || cb === void 0 ? void 0 : cb(null);
            return null;
        }
        for (const dialogName in availableDialogs) {
            const dialog = availableDialogs[dialogName];
            if (dialog.componentType === type) {
                cb === null || cb === void 0 ? void 0 : cb(dialog);
                return dialog;
            }
        }
        cb === null || cb === void 0 ? void 0 : cb(null);
        return null;
    }
    /**
     * State management - just moves an opened dialog back to the "available" pool.
     * @param {object} identifier window identifier of the dialog.
     */
    async moveDialogFromOpenedToAvailable(identifier) {
        var _a, _b;
        if (!this.DialogStore) {
            const error = `DialogMangerClient.moveDialogFromOpenedToAvailable() store not yet initialized}`;
            Logger.system.error(error);
            return error;
        }
        const { err, data: dialogs } = await this.DialogStore.getGlobal(["dialogs"]);
        if (err || !dialogs) {
            const error = `DialogManagerClient.moveDialogFromOpenedToAvailable() no "dialogs" object returned from store`;
            Logger.system.error(error);
            return error;
        }
        const openedDialogs = dialogs.opened;
        const availableDialogs = dialogs.available;
        delete openedDialogs[identifier.windowName];
        availableDialogs[identifier.windowName] = identifier;
        await ((_a = this.DialogStore) === null || _a === void 0 ? void 0 : _a.set(["dialogs", "opened"], openedDialogs));
        await ((_b = this.DialogStore) === null || _b === void 0 ? void 0 : _b.set(["dialogs", "available"], availableDialogs));
        return null;
    }
    /**
     * State management - just moves an available dialog out of the "available" pool.
     * @param {object} identifier window identifier of the dialog.
     */
    async moveDialogFromAvailableToOpened(identifier) {
        var _a, _b;
        if (!this.DialogStore) {
            const error = `DialogMangerClient.moveDialogFromAvailableToOpened() store not yet initialized}`;
            Logger.system.error(error);
            return error;
        }
        const { err, data: dialogs } = await this.DialogStore.getGlobal(["dialogs"]);
        if (err || !dialogs) {
            const error = `DialogManagerClient.moveDialogFromAvailableToOpened no "dialogs" object returned from store`;
            Logger.system.error(error);
            return error;
        }
        const availableDialogs = dialogs.available;
        const openedDialogs = dialogs.opened;
        delete availableDialogs[identifier.windowName];
        openedDialogs[identifier.windowName] = identifier;
        await ((_a = this.DialogStore) === null || _a === void 0 ? void 0 : _a.set(["dialogs", "available"], availableDialogs));
        await ((_b = this.DialogStore) === null || _b === void 0 ? void 0 : _b.set(["dialogs", "opened"], openedDialogs));
        return null;
    }
}
//# sourceMappingURL=DialogManagerClientController.js.map