/*!
 * Copyright 2017 - 2020 by ChartIQ, Inc.
 * All rights reserved.
 */
/**
 * The Distributed Store Client handles creating, retrieving, and destroying stores. Stores are used to save and retrieve data either locally or globally.
 * This data is not persisted. You can add listeners at multiple levels (store or field), and get the updated data as it's updated in the store.
 * Fields are stored within the store as key/value pair. For more information, see the <a href="/docs/productivity/API-tutorials-and-refs/storage/DistributedStore">Distributed Store tutorial</a>.
 * @module DistributedStoreClient
 */
/**
This is how we want our API to work going forward, the way the world does it: `import { processAndSet } from "configClient"`
```
export const processAndSet = () => { ... }
```

This module's single exported object gives us backward compatibility when our core code does this: `import { ConfigClient } from "configClient"`
and provides the object that is attached to FSBL.Clients. This export should be kept as clean and simple as possible.
```
export const ConfigClient = {
   processAndSet
}
```

The default export gives us backward compatibility where our core code does this: import ConfigClient from "configClient"
This can be eliminated when our core core is converted to normal imports as in the first example.
```
export default ConfigClient;
```
*/
import { Logger } from "./logger";
import StoreModel from "./StoreModel";
import SystemManagerClient from "../common/systemManagerClient";
import { RouterClient } from "../clients/routerClient";
export { StoreModel };
/**
 * Persistent stores are loaded asynchronously within the distributed store service. The
 * `distributedStoresLoading` responder publishes its status. It publishes State="done"
 * when the persistent stores are loaded from storage.
 */
const persistentStoresAreReady = () => new Promise((resolve) => {
    RouterClient.subscribe("distributedStoresLoading", (err, response) => {
        if (err || !response.data) {
            const error = `Error waiting for persistent stores ${err}`;
            Logger.system.error(error);
            resolve(error);
        }
        if (response.data.State === "done") {
            resolve(null);
        }
    });
});
let localStore = {};
const removeGlobalStore = async (params, cb) => {
    const { err } = await RouterClient.query("storeService.removeStore", params);
    if (cb)
        cb(err, !!err);
    return { err, data: !!err };
};
/**
 * @language javascript, java, dotnet
 * Remove a store. If global is not set and a local store isn't found, Finsemble will remove the global store.
 *
 * ```javascript
 * FSBL.Clients.DistributedStoreClient.removeStore({
 * 	store:"store1",
 * 	global:true
 * },
 * function(){});
 * ```
 * @param params.store The name of the store.
 * @param params.global Whether the store you're trying to remove is a global store.
 * @param cb Callback to be invoked when the store is removed.
 */
export const removeStore = async (params, cb) => {
    if (params.global) {
        return removeGlobalStore(params, cb);
    }
    else {
        if (localStore[params.store]) {
            delete localStore[params.store];
            if (cb)
                cb(null, true);
            return { data: true };
        }
        else {
            return { err: `Local store named ${params.store} not found` };
        }
    }
};
const getGlobalStore = async (params, cb) => {
    Logger.system.info("DistributedStoreClient:getGlobalStore()", params);
    if (params.waitForPersistentStoreToLoad) {
        const waitError = await persistentStoresAreReady();
        if (waitError) {
            cb === null || cb === void 0 ? void 0 : cb(waitError);
            return { err: waitError };
        }
    }
    else {
        await SystemManagerClient.waitForStartup("dataStoreService");
    }
    const { err, response } = await RouterClient.query("storeService.getStore", params);
    if (response.data) {
        const newStore = new StoreModel(response.data, RouterClient, removeStore);
        if (cb)
            cb(err, newStore);
        return { err, data: newStore };
    }
    else {
        Logger.system.error("DistributedStoreClient:getGlobalStore() Failed to get a global store", params.store, err);
        if (cb)
            cb(err);
        return { err };
    }
};
/**
 * @language javascript, java, dotnet
 * Retrieve a store if it exists in the local scope, otherwise from the global scope.
 *
 * ```javascript
 * FSBL.Clients.DistributedStoreClient.getStore({
 * 	store:'store1'
 * },
 * function(error, storeObject){});
 * ```
 * @param params
 * @param params.store The name of the store.
 * @param params.global Get the store only from the global scope.
 * @param params.waitForPersistentStoreToLoad Set this to true if you know you are getting a reference to a persistent [global] store
 * @param cb -  Will return the value if found.
 * @returns Returns the store
 */
export const getStore = async (params, cb) => {
    if (params.global) {
        return getGlobalStore(params, cb);
    }
    else {
        const store = params.store ? localStore[params.store] : null;
        if (store) {
            if (cb)
                cb(null, store);
            return { data: store };
        }
        else {
            return getGlobalStore(params, cb);
        }
    }
};
/**
 * @private Use createStore();
 * @ignore
 */
export const createGlobalStore = async (params, cb) => {
    // If this is a persistent store then wait for the store to be loaded
    // otherwise just wait for the service to start
    if (params.persist) {
        const waitError = await persistentStoresAreReady();
        if (waitError) {
            cb === null || cb === void 0 ? void 0 : cb(waitError);
            return { err: waitError };
        }
    }
    else {
        await SystemManagerClient.waitForStartup("dataStoreService");
    }
    const { err, response } = await RouterClient.query("storeService.createStore", params);
    if (err) {
        Logger.system.error("DistributedStoreClient:createStore() Failed to create a global store", params.store, err);
        cb === null || cb === void 0 ? void 0 : cb(err);
        return { err };
    }
    const data = new StoreModel(response.data, RouterClient, removeStore);
    cb === null || cb === void 0 ? void 0 : cb(err, data);
    return { err, data };
};
/**
 * @private Use createStore()
 */
const createLocalStore = async (params, cb) => {
    // If the local store already exists then return it
    const data = localStore[params.store];
    if (data) {
        if (cb)
            cb(null, data);
        return { data };
    }
    // Create a new local store
    const newLocalStore = new StoreModel({
        ...params,
        name: params.store,
    }, RouterClient, removeStore);
    localStore[newLocalStore.name] = newLocalStore;
    if (cb)
        cb(null, newLocalStore);
    return { data: newLocalStore };
};
/**
 * @language javascript, java, dotnet
 *Creates a store.

    * ```javascript
    * FSBL.Clients.DistributedStoreClient.createStore({
    * 	store:"store1",
    * 	global:false,
    *      persist: false,
    * 	values:{}
    * },
    * function(error, storeObject){});
    * ```
    * @param params.store The name of the store.
    * @param params.values Starting values for the store.
    * @param params.global Whether a store is accessible outside of the component it's created in.
    * @param params.persist Whether to persist the values of the store to storage. The store must be global to use this flag.
    * @param cb  Will return the store on success.
    * @returns Callback will receive the store
    */
export const createStore = async (params, cb) => {
    Logger.system.info("DistributedStoreClient:createStore()", params);
    if (params.global) {
        return createGlobalStore(params, cb);
    }
    else {
        return createLocalStore(params, cb);
    }
};
/**
 * @ignore
 */
export const DistributedStoreClient = {
    /**
     * For backward compatibility
     * @private
     * @deprecated
     */
    initialize: () => {
        Logger.system.deprecated("It is no longer necessary to call DistributedStoreClient.initialize()");
    },
    /**
     * For backward compatibility
     * @private
     * @deprecated
     */
    onReady: (cb) => {
        Logger.system.deprecated("It is no longer necessary to call DistributedStoreClient.onReady()");
        cb === null || cb === void 0 ? void 0 : cb();
    },
    createStore,
    getStore,
    createGlobalStore,
    removeStore,
};
/**
 * @ignore
 */
export default DistributedStoreClient;
//# sourceMappingURL=distributedStoreClient.js.map