import { html } from "htm/preact";

// TODO: CSS
//import './ANSIDisplay.css';

import { default as ansi } from "./ansi-output.js";

export const ANSIDisplay = ({ output }) => {
  const ansiOutput = new ansi.ANSIOutput();
  ansiOutput.processOutput(output);

  let firstOutput = false;
  return html`<div class="ansi-display">
    ${ansiOutput.outputLines.map((line) => {
      firstOutput = firstOutput || !!line.outputRuns.length;
      return html`<div class="ansi-display-line">
        ${!line.outputRuns.length
          ? firstOutput
            ? html`<br />`
            : null
          : line.outputRuns.map(
              (outputRun) =>
                html`<${OutputRun}
                  key=${outputRun.id}
                  outputRun=${outputRun}
                />`,
            )}
      </div>`;
    })}
  </div>`;
};

const kForeground = 0;
const kBackground = 1;

const OutputRun = ({ outputRun }) => {
  const computeStyles = (styles) => {
    let cssProperties = {};
    if (styles) {
      styles.forEach((style) => {
        switch (style) {
          // Bold.
          case ansi.ANSIStyle.Bold:
            cssProperties = { ...cssProperties, ...{ fontWeight: "bold" } };
            break;

          // Dim.
          case ansi.ANSIStyle.Dim:
            cssProperties = { ...cssProperties, ...{ fontWeight: "lighter" } };
            break;

          // Italic.
          case ansi.ANSIStyle.Italic:
            cssProperties = { ...cssProperties, ...{ fontStyle: "italic" } };
            break;

          // Underlined.
          case ansi.ANSIStyle.Underlined:
            cssProperties = {
              ...cssProperties,
              ...{
                textDecorationLine: "underline",
                textDecorationStyle: "solid",
              },
            };
            break;

          // Slow blink.
          case ansi.ANSIStyle.SlowBlink:
            cssProperties = {
              ...cssProperties,
              ...{ animation: "ansi-display-run-blink 1s linear infinite" },
            };
            break;

          // Rapid blink.
          case ansi.ANSIStyle.RapidBlink:
            cssProperties = {
              ...cssProperties,
              ...{ animation: "ansi-display-run-blink 0.5s linear infinite" },
            };
            break;

          // Hidden.
          case ansi.ANSIStyle.Hidden:
            cssProperties = { ...cssProperties, ...{ visibility: "hidden" } };
            break;

          // CrossedOut.
          case ansi.ANSIStyle.CrossedOut:
            cssProperties = {
              ...cssProperties,
              ...{
                textDecorationLine: "line-through",
                textDecorationStyle: "solid",
              },
            };
            break;

          // TODO Fraktur

          // DoubleUnderlined.
          case ansi.ANSIStyle.DoubleUnderlined:
            cssProperties = {
              ...cssProperties,
              ...{
                textDecorationLine: "underline",
                textDecorationStyle: "double",
              },
            };
            break;

          // TODO Framed
          // TODO Encircled
          // TODO Overlined
          // TODO Superscript
          // TODO Subscript
        }
      });
    }

    return cssProperties;
  };

  const computeForegroundBackgroundColor = (colorType, color) => {
    switch (color) {
      // Undefined.
      case undefined:
        return {};

      // One of the standard colors.
      case ansi.ANSIColor.Black:
      case ansi.ANSIColor.Red:
      case ansi.ANSIColor.Green:
      case ansi.ANSIColor.Yellow:
      case ansi.ANSIColor.Blue:
      case ansi.ANSIColor.Magenta:
      case ansi.ANSIColor.Cyan:
      case ansi.ANSIColor.White:
      case ansi.ANSIColor.BrightBlack:
      case ansi.ANSIColor.BrightRed:
      case ansi.ANSIColor.BrightGreen:
      case ansi.ANSIColor.BrightYellow:
      case ansi.ANSIColor.BrightBlue:
      case ansi.ANSIColor.BrightMagenta:
      case ansi.ANSIColor.BrightCyan:
      case ansi.ANSIColor.BrightWhite:
        if (colorType === kForeground) {
          return { color: `var(--${color})` };
        } else {
          return { background: `var(--${color})` };
        }

      // TODO@softwarenerd - This isn't hooked up.
      default:
        if (colorType === kForeground) {
          return { color: color };
        } else {
          return { background: color };
        }
    }
  };

  const computeCSSProperties = (outputRun) => {
    return !outputRun.format
      ? {}
      : {
          ...computeStyles(outputRun.format.styles),
          ...computeForegroundBackgroundColor(
            kForeground,
            outputRun.format.foregroundColor,
          ),
          ...computeForegroundBackgroundColor(
            kBackground,
            outputRun.format.backgroundColor,
          ),
        };
  };

  // Render.
  return html`<span style=${computeCSSProperties(outputRun)}
    >${outputRun.text}</span
  >`;
};
