import { Thread } from '../Thread.mjs';
import { windowToThreadTarget } from './window/ThreadWindow.mjs';
import { ThreadNestedIframe } from './ThreadNestedIframe.mjs';

/**
 * Creates a thread from an iframe element.
 *
 * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
 *
 * @example
 * import {ThreadIframe} from '@quilted/threads';
 *
 * const iframe = document.querySelector('iframe#thread-iframe');
 * const thread = new ThreadIframe(iframe);
 * await thread.imports.sendMessage('Hello world!');
 */
class ThreadIframe extends Thread {
  /**
   * Creates a thread from an iframe element.
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe
   *
   * @example
   * import {ThreadIframe} from '@quilted/threads';
   *
   * const iframe = document.querySelector('iframe#thread-iframe');
   * const thread = ThreadIframe.from(iframe);
   * await thread.imports.sendMessage('Hello world!');
   */
  static from(iframe, options) {
    return new ThreadIframe(iframe, options);
  }

  /**
   * Starts a thread wrapped around an iframe element, and returns the imports
   * of the thread.
   *
   * @example
   * ```ts
   * import {ThreadIframe} from '@quilted/threads';
   *
   * const {getMessage} = ThreadIframe.import(iframe);
   * const message = await getMessage(); // 'Hello, world!'
   *
   * // In the nested window:
   *
   * import {ThreadIframe} from '@quilted/threads';
   *
   * ThreadIframe.parent.export({
   *   async getMessage() {
   *     return 'Hello, world!';
   *   },
   * });
   * ```
   */
  static import(iframe, options) {
    return new ThreadIframe(iframe, options).imports;
  }

  /**
   * Starts a thread wrapped around an iframe element, providing the second
   * argument as the exports of the thread.
   *
   * @example
   * ```ts
   * import {ThreadIframe} from '@quilted/threads';
   *
   * ThreadIframe.export(iframe, {
   *   async getMessage() {
   *     return 'Hello, world!';
   *   },
   * });
   *
   * // In the nested iframe:
   *
   * import {ThreadIframe} from '@quilted/threads';
   *
   * const {getMessage} = ThreadIframe.parent.import();
   * const message = await getMessage(); // 'Hello, world!'
   * ```
   */
  static export(iframe, exports, options) {
    new ThreadIframe(iframe, {
      ...options,
      exports
    });
  }

  /**
   * Creates a thread from a `window.parent`, a reference to the parent window when rendering
   * in a nested iframe.
   *
   * @see https://developer.mozilla.org/en-US/docs/Web/API/Window/parent
   *
   * @example
   * import {ThreadIframe} from '@quilted/threads';
   *
   * const thread = ThreadIframe.parent();
   * await thread.imports.sendMessage('Hello world!');
   */
  static parent = (() => Object.assign(options => new ThreadNestedIframe(options), {
    /**
     * Starts a thread wrapped around a parent window, and returns the imports
     * of the thread.
     *
     * @example
     * ```ts
     * import {ThreadIframe} from '@quilted/threads';
     *
     * const {getMessage} = ThreadIframe.parent.import();
     * const message = await getMessage(); // 'Hello, world!'
     *
     * // In the parent window:
     *
     * import {ThreadIframe} from '@quilted/threads';
     *
     * ThreadIframe.export(iframe, {
     *   async getMessage() {
     *     return 'Hello, world!';
     *   },
     * });
     * ```
     */
    import: options => ThreadNestedIframe.import(options),
    /**
     * Starts a thread wrapped around a parent window, providing the second
     * argument as the exports of the thread.
     *
     * @example
     * ```ts
     * import {ThreadIframe} from '@quilted/threads';
     *
     * ThreadIframe.parent.export({
     *   async getMessage() {
     *     return 'Hello, world!';
     *   },
     * });
     *
     * // In the parent window:
     *
     * import {ThreadIframe} from '@quilted/threads';
     *
     * const {getMessage} = ThreadIframe.import(iframe);
     * const message = await getMessage(); // 'Hello, world!'
     * ```
     */
    export: (exports, options) => ThreadNestedIframe.export(exports, options)
  }))();
  constructor(iframe, {
    targetOrigin = '*',
    ...options
  } = {}) {
    super(windowToThreadTarget(iframe.contentWindow, {
      targetOrigin
    }), options);
    this.iframe = iframe;
  }
}

export { ThreadIframe };
