/**
 * @license
 * Copyright 2022 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import {HighlightJS} from '../types/types';
import {
  SyntaxWorkerMessage,
  SyntaxWorkerResult,
  isRequest,
  isInit,
} from '../types/syntax-worker-api';
import {highlightedStringToRanges} from '../utils/syntax-util';
import {importScript} from '../utils/worker-util';

// This is an entry point file of a bundle. Keep free of exports!

/**
 * This is a web worker for calling the HighlightJS library for syntax
 * highlighting. Files can be large and highlighting does not require
 * the `document` or the `DOM`, so it is a perfect fit for a web worker.
 *
 * This file is a just a hub hooking into the web worker API. The message
 * events for communicating with the main app are defined in the file
 * `types/worker-api.ts`. And the `meat` of the computation is done in the
 * file `syntax-util.ts`.
 */

/**
 * `self` is for a worker what `window` is for the web app. It is called
 * the `DedicatedWorkerGlobalScope`, see
 * https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope
 *
 * Once imported the HighlightJS lib exposes its functionality via the global
 * `hljs` variable.
 */
const ctx = self as DedicatedWorkerGlobalScope & {hljs?: HighlightJS};

/**
 * We are encapsulating the web worker API here, so this is the only place
 * where you need to know about it and the MessageEvents in this file.
 */
ctx.onmessage = function (e: MessageEvent<SyntaxWorkerMessage>) {
  try {
    const message = e.data;
    if (isInit(message)) {
      worker.init(message.url);
      const result: SyntaxWorkerResult = {ranges: []};
      ctx.postMessage(result);
    }
    if (isRequest(message)) {
      const ranges = worker.highlightCode(message.language, message.code);
      const result: SyntaxWorkerResult = {ranges};
      ctx.postMessage(result);
    }
  } catch (err) {
    let error = 'syntax worker error';
    if (err instanceof Error) error = err.message;
    const result: SyntaxWorkerResult = {error, ranges: []};
    ctx.postMessage(result);
  }
};

class SyntaxWorker {
  private highlightJsLib?: HighlightJS;

  init(highlightJsLibUrl: string) {
    importScript(ctx, highlightJsLibUrl);
    if (!ctx.hljs) {
      throw new Error('HighlightJS lib not available after import');
    }
    this.highlightJsLib = ctx.hljs;
    this.highlightJsLib.configure({classPrefix: ''});
  }

  highlightCode(language: string, code: string) {
    if (!this.highlightJsLib) throw new Error('worker not initialized');
    const highlighted = this.highlightJsLib.highlight(language, code, true);
    return highlightedStringToRanges(highlighted.value);
  }
}

/** Singleton instance being referenced in `onmessage` function above. */
const worker = new SyntaxWorker();
