/*!
 * Copyright 2017 - 2020 by ChartIQ, Inc.
 * All rights reserved.
 */
/**
 * The Search Client allows for any window launched by Finsemble to act as a search provider or query against the registered providers.
 *
 * See the <a href="/docs/productivity/API-tutorials-and-refs/search">Search tutorial</a> for an overview of using the Search Client.
 * @module SearchClient
 */
/**
This is how we want our API to work going foward, 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 { Startup } from "../clients/Startup/Startup";
import { RouterClient } from "../clients/routerClient";
import { singleton } from "../common/Singleton";
import SystemManagerClient from "../common/systemManagerClient";
Logger.system.debug("Starting searchClient");
const registerItemActionResponder = (channel, handler) => {
    RouterClient.addResponder(`Search.Provider.ItemAction.${channel}`, (err, message) => {
        if (err || !message) {
            Logger.system.error(`SearchClient error registering responder for "Search.Provider.ItemAction.${channel}"`, err);
            return;
        }
        handler(message.data, message.data.windowName, (err2, res) => {
            message.sendQueryResponse(err2, res);
        });
    });
};
const registerProviderActionResponder = (channel, handler) => {
    RouterClient.addResponder(`Search.Provider.Action.${channel}`, (err, message) => {
        if (err || !message) {
            Logger.system.error(`SearchClient error registering responder for "Search.Provider.Action.${channel}"`, err);
            return;
        }
        handler(message.data.windowName, (err2, res) => {
            message.sendQueryResponse(err2, res);
        });
    });
};
class SearchClientController {
    constructor() {
        this.searchId = null;
        this.resultsCallback = null;
        this.providers = {};
        this.resultProviders = {};
        this.searchResultsList = [];
        this.isSearching = false; // We use this so we don't create multiple responders for every window that searches.
    }
}
const getControllerInstance = () => singleton("SearchClient", SearchClientController);
/**
 * This handles our results when we get them back from a provider
 */
const handleResults = (err, message) => {
    var _a;
    const controller = getControllerInstance();
    if (!message.data.searchId || message.data.searchId != controller.searchId)
        return;
    controller.resultProviders[message.data.provider.channel] = message.data;
    controller.searchResultsList.push(message.data);
    (_a = controller.resultsCallback) === null || _a === void 0 ? void 0 : _a.call(controller, null, controller.searchResultsList);
};
/**
 * @language javascript, java, dotnet
 * Register a provider with the search service.
 *
 *  `{ status : "success" }` is resolved or called back when successful.
 *
 * ```javascript
 * FSBL.Clients.SearchClient.register({
 *		name: "MyProvider",
 *		searchCallback: searchApplications,
 *		itemActionCallback: itemActionCallback,
 *		providerActionTitle: providerActionTitle,
 *		providerActionCallback:providerActionCallback
 *	},
 *	(err, response) => {
 * 		//provider has been registered
 * 	});
 * ```
 * @param params - Params object
 * @param params.name - The name of the provider.
 * @param params.searchCallback - A function called when a search is initialized.
 * @param params.itemActionCallback - A function that is called when an item action is fired.
 * @param params.providerActionCallback - A function that is called when a provider action is fired.
 * @param params.providerActionTitle - The title of the provider action.
 * @param cb - Callback to be invoked when the provider is registered.
 */
export const register = async (params, cb) => {
    if (!params.name) {
        const error = `"name" is required in params`;
        cb === null || cb === void 0 ? void 0 : cb(error);
        return { err: error };
    }
    if (!params.searchCallback) {
        const error = `"searchCallback" is required in params`;
        cb === null || cb === void 0 ? void 0 : cb(error);
        return { err: error };
    }
    await SystemManagerClient.waitForStartup("searchService");
    const { windowName } = Startup.getInstance().getStartupData();
    const controller = getControllerInstance();
    const { name, providerActionCallback, itemActionCallback, searchCallback } = params;
    const channel = `${windowName}.${name}`;
    const { err: registerErr, response } = await RouterClient.query("Search.register", {
        name,
        channel,
        providerActionTitle: params.providerActionTitle,
        providerActionCallback: !!params.providerActionCallback,
    });
    if (registerErr) {
        cb === null || cb === void 0 ? void 0 : cb(registerErr);
        return { err: registerErr };
    }
    controller.providers[name] = name;
    // This is where we receive our search requests.
    RouterClient.addResponder(`Search.Provider.${channel}`, (err, message) => {
        if (err || !message)
            return;
        searchCallback(message.data, (searchCallbackError, res) => {
            message.sendQueryResponse(searchCallbackError, res);
        });
    });
    // Optional item action and provider action responders
    if (itemActionCallback)
        registerItemActionResponder(channel, itemActionCallback);
    if (providerActionCallback)
        registerProviderActionResponder(channel, providerActionCallback);
    const { data } = response;
    cb === null || cb === void 0 ? void 0 : cb(null, data);
    return { data };
};
/**
 * @language javascript, java, dotnet
 * Remove a provider. This can only be done from the window that registered the provider.
 *
 * ```javascript
 * FSBL.Clients.SearchClient.unregister({ name: "MyProvider" }, function(){ });
 * ```
 * @param params
 * @param params.name - The name of the provider to be removed.
 * @param cb The callback to be invoked after the method completes successfully.
 */
export const unregister = async (params, cb) => {
    if (!params.name)
        return cb === null || cb === void 0 ? void 0 : cb("Provider name was not provided");
    const { windowName } = Startup.getInstance().getStartupData();
    const provider = `${windowName}.${params.name}`;
    await RouterClient.query("Search.unregister", { channel: provider });
    const controller = getControllerInstance();
    RouterClient.removeResponder(`Search.Provider.${provider}`);
    RouterClient.removeResponder(`Search.Provider.ItemAction.${provider}`);
    RouterClient.removeResponder(`Search.Provider.Action.${provider}`);
    delete controller.providers[params.name];
    cb === null || cb === void 0 ? void 0 : cb();
};
/**
 * @language javascript, java, dotnet
 * This initiates a search.
 *
 * The promise will resolve when the search has been successfully initiated but results are only returned on
 * the callback handler. This callback handler may be called *multiple times*. Each time, the complete set of
 * search results is returned. There is no indication when all results have returned.
 *
 * ```javascript
 * FSBL.Clients.SearchClient.search({
 *		text: "Chart",
 * }, (err, response) => {
 * 		// Search results will be returned here
 * });
 * ```
 * @param params - Params object
 * @param params.text - The text to be searched
 * @param params.windowName Optional. Will be set to the window which is invoking the API method.
 * @param cb - Callback to be called as search results for each provider are returned. Results are combined as they come in.
 * So every response will have the complete list of results that have been returned. Example: You have two providers; provider one returns results first: you'll have an array with just provider one's data. Once provider
 * two returns, you'll have results for provider one and provider two.
 */
export const search = async (params, cb) => {
    await SystemManagerClient.waitForStartup("searchService");
    const controller = getControllerInstance();
    const { windowName } = Startup.getInstance().getStartupData();
    if (!controller.isSearching) {
        RouterClient.addPubSubResponder(`Search.${windowName}`);
        RouterClient.subscribe(`Search.${windowName}`, handleResults);
        controller.isSearching = true;
    }
    controller.searchResultsList = [];
    params.windowName = windowName;
    const { err, response } = await RouterClient.query("Search.search", params);
    if (err) {
        return cb(err);
    }
    controller.resultsCallback = cb;
    controller.searchId = response.data.searchId;
};
/**
 * @language javascript, java, dotnet
 * Call this when you want to trigger an action associated to a returned item. There can be multiple actions associated with a result item and only one should be fired at a time.
 *
 * ```javascript
 * FSBL.Clients.SearchClient.invokeItemAction(resultItem, action);
 * ```
 * @param item - This is the search result item.
 * @param action - The action object
 * @param action.name - The name of the action that you would like to fire.
 */
export const invokeItemAction = (item, action) => {
    RouterClient.query(`Search.Provider.ItemAction.${item.provider}`, {
        item,
        action,
        windowName: Startup.getInstance().currentStartupData.windowName,
    });
};
/**
 * @language javascript, java, dotnet
 * @deprecated
 * Call this when you want to trigger an action associated with a provider.
 * Not all providers register an action handler.
 *
 * ```javascript
 * FSBL.Clients.SearchClient.invokeProviderAction(provider);
 * ```
 * @param provider Object containing the information needed to trigger an action.
 * @param provider.channel The channel identifier which is being requested to conduct it's action
 */
export const invokeProviderAction = (provider) => {
    Logger.system.deprecated("SearchClient.invokeProviderAction() is deprecated.");
    RouterClient.query(`Search.Provider.Action.${provider.channel}`, {
        windowName: Startup.getInstance().currentStartupData.windowName,
    });
};
/**
 * @ignore
 */
export const SearchClient = {
    /**
     * For backward compatibility
     * @private
     * @deprecated
     */
    initialize: () => {
        Logger.system.deprecated("It is no longer necessary to call SearchClient.initialize()");
    },
    /**
     * For backward compatibility
     * @private
     * @deprecated
     */
    onReady: (cb) => {
        Logger.system.deprecated("It is no longer necessary to call SearchClient.onReady()");
        cb === null || cb === void 0 ? void 0 : cb();
    },
    invokeProviderAction,
    invokeItemAction,
    search,
    unregister,
    register,
};
/**
 * @ignore
 */
export default SearchClient;
//# sourceMappingURL=searchClient.js.map