/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

"use strict";

const DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME = "DevToolsWorker";

/**
 * Force creating targets for all existing workers for a given Watcher Actor.
 *
 * @param WatcherActor watcher
 *        The Watcher Actor requesting to watch for new targets.
 */
async function createTargets(watcher) {
  // Go over all existing BrowsingContext in order to:
  // - Force the instantiation of a DevToolsWorkerChild
  // - Have the DevToolsWorkerChild to spawn the WorkerTargetActors
  const browsingContexts = watcher.getAllBrowsingContexts({
    acceptSameProcessIframes: true,
    forceAcceptTopLevelTarget: true,
  });
  const promises = [];
  for (const browsingContext of browsingContexts) {
    const promise = browsingContext.currentWindowGlobal
      .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME)
      .instantiateWorkerTargets({
        watcherActorID: watcher.actorID,
        connectionPrefix: watcher.conn.prefix,
        sessionContext: watcher.sessionContext,
        sessionData: watcher.sessionData,
      });
    promises.push(promise);
  }

  // Await for the different queries in order to try to resolve only *after* we received
  // the already available worker targets.
  return Promise.all(promises);
}

/**
 * Force destroying all worker targets which were related to a given watcher.
 *
 * @param WatcherActor watcher
 *        The Watcher Actor requesting to stop watching for new targets.
 */
async function destroyTargets(watcher) {
  // Go over all existing BrowsingContext in order to destroy all targets
  const browsingContexts = watcher.getAllBrowsingContexts({
    acceptSameProcessIframes: true,
    forceAcceptTopLevelTarget: true,
  });
  for (const browsingContext of browsingContexts) {
    let windowActor;
    try {
      windowActor = browsingContext.currentWindowGlobal.getActor(
        DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME
      );
    } catch (e) {
      continue;
    }

    windowActor.destroyWorkerTargets({
      watcherActorID: watcher.actorID,
      sessionContext: watcher.sessionContext,
    });
  }
}

/**
 * Go over all existing BrowsingContext in order to communicate about new data entries
 *
 * @param WatcherActor watcher
 *        The Watcher Actor requesting to stop watching for new targets.
 * @param string type
 *        The type of data to be added
 * @param Array<Object> entries
 *        The values to be added to this type of data
 */
async function addSessionDataEntry({ watcher, type, entries }) {
  const browsingContexts = watcher.getAllBrowsingContexts({
    acceptSameProcessIframes: true,
    forceAcceptTopLevelTarget: true,
  });
  const promises = [];
  for (const browsingContext of browsingContexts) {
    const promise = browsingContext.currentWindowGlobal
      .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME)
      .addSessionDataEntry({
        watcherActorID: watcher.actorID,
        sessionContext: watcher.sessionContext,
        type,
        entries,
      });
    promises.push(promise);
  }
  // Await for the queries in order to try to resolve only *after* the remote code processed the new data
  return Promise.all(promises);
}

/**
 * Notify all existing frame targets that some data entries have been removed
 *
 * See addSessionDataEntry for argument documentation.
 */
function removeSessionDataEntry({ watcher, type, entries }) {
  const browsingContexts = watcher.getAllBrowsingContexts({
    acceptSameProcessIframes: true,
    forceAcceptTopLevelTarget: true,
  });
  for (const browsingContext of browsingContexts) {
    browsingContext.currentWindowGlobal
      .getActor(DEVTOOLS_WORKER_JS_WINDOW_ACTOR_NAME)
      .removeSessionDataEntry({
        watcherActorID: watcher.actorID,
        sessionContext: watcher.sessionContext,
        type,
        entries,
      });
  }
}

module.exports = {
  createTargets,
  destroyTargets,
  addSessionDataEntry,
  removeSessionDataEntry,
};
