/**
 * @license
 * Copyright 2022-2026 Greg Lauckhart <greg@lauckhart.com>
 * SPDX-License-Identifier: Apache-2.0
 */
import { ContiguousOutputSegment } from "./contiguous-output-segment.js";
import { Producer } from "./producer.js";
import { Style } from "./style.js";
import { tokenize } from "./tokenize.js";
const NEWLINE = { kind: "newline" };
const SPACE = { kind: "nonbreaking", str: " ", width: 1 };
class Wrapper {
  #target;
  #wrapPrefix;
  #preserveIndent;
  #preserveSpace;
  #splitStyling;
  #inputState = "newline";
  #outputState = "newline";
  #indent;
  #output;
  #onRevert = () => {
    this.#emit();
  };
  constructor(target, options) {
    const { wrapPrefix, preserveIndent, splitStyling, preserveSpace } = options;
    this.#target = target;
    if (wrapPrefix !== void 0) {
      this.#wrapPrefix = new ContiguousOutputSegment(...tokenize(wrapPrefix));
    }
    this.#preserveIndent = preserveIndent ?? true;
    this.#preserveSpace = preserveSpace ?? true;
    this.#splitStyling = splitStyling ?? !!(this.#preserveIndent || this.#wrapPrefix || !this.#target.state.linePrefix);
    this.#target.state.onRevert(this.#onRevert);
  }
  get state() {
    return this.#target.state;
  }
  write(...text) {
    if (this.#target.state.terminalWidth === void 0 || this.#target.state.wrap === false) {
      this.#target.write(...text);
      return;
    }
    for (const token of Producer.of(text)) {
      this.#addToken(token);
    }
  }
  close() {
    this.#emit();
    this.#target.state.offRevert(this.#onRevert);
    this.#target.close();
  }
  [Symbol.dispose]() {
    this.close();
  }
  #addToken(token) {
    switch (token.kind) {
      case "newline":
      case "carriage-return":
        this.#emit();
        this.#target.write(token);
        this.#indent = void 0;
        this.#outputState = this.#inputState = "newline";
        break;
      case "ansi":
        switch (token.sequence) {
          case Wrapper.prefixStart:
            this.#inputState = "prefix";
            break;
          case Wrapper.prefixStop:
            this.#inputState = "indent";
            break;
          default:
            this.#enqueue(token);
            break;
        }
        break;
      case "style":
        this.#enqueue(token);
        break;
      case "nonbreaking":
        if (this.#inputState === "prefix") {
          this.#enqueue(token);
          break;
        }
        if (this.#inputState === "indent") {
          this.#indent = this.#output;
          this.#output = void 0;
        }
        this.#inputState = "word";
        this.#enqueue(token);
        break;
      case "tab":
      case "breaking":
        switch (this.#inputState) {
          case "newline":
            if (this.#preserveIndent) {
              this.#enqueue(token);
            }
            this.#inputState = "indent";
            break;
          case "prefix":
          case "indent":
            this.#enqueue(token);
            break;
          case "word":
          case "space":
            if (this.#preserveSpace) {
              this.#enqueue(token);
            }
            this.#emit();
            this.#inputState = "space";
            break;
        }
        break;
    }
  }
  #emit() {
    if (this.#output === void 0) {
      return;
    }
    const outputState = this.#outputState;
    if (outputState === "newline") {
      this.#emitPrefix();
    }
    this.#outputState = "inline";
    const output = this.#output;
    this.#output = void 0;
    let remainingWidth = this.#target.state.remainingWidth;
    if (!this.#preserveSpace && remainingWidth !== void 0 && outputState === "inline") {
      remainingWidth--;
    }
    if (remainingWidth === void 0 || output.width <= remainingWidth) {
      if (outputState === "inline" && !this.#preserveSpace) {
        this.#target.write(SPACE);
      }
      this.#target.write(...output.tokens);
      return;
    }
    const wrapPrefixWidth = this.#effectiveWrapPrefixWidth;
    const availableWrappedWidth = this.#target.state.availableWidth - wrapPrefixWidth;
    if (outputState === "newline" && remainingWidth <= 0 || availableWrappedWidth < 0) {
      this.#target.write(...output.tokens);
      return;
    }
    if (output.width < availableWrappedWidth) {
      this.#wrap();
      this.#target.write(...output.tokens);
      return;
    }
    const scanner = output.scan();
    if (outputState === "inline" && !this.#preserveSpace) {
      this.#target.write(SPACE);
    }
    this.#target.write(...scanner.take(remainingWidth));
    while (!scanner.done) {
      this.#wrap();
      this.#target.write(...scanner.take(availableWrappedWidth));
    }
  }
  get #effectiveWrapPrefixWidth() {
    const wrapIndent = this.#target.state.wrapIndent;
    if (wrapIndent !== void 0) {
      return wrapIndent;
    }
    return (this.#wrapPrefix?.width ?? 0) + (this.#indent?.width ?? 0);
  }
  #emitPrefix(extra) {
    if (this.#indent !== void 0) {
      this.#target.write(...this.#indent.tokens);
    }
    if (extra !== void 0) {
      this.#target.write(...extra.tokens);
    }
  }
  #wrap() {
    let style;
    if (this.#splitStyling) {
      style = this.#target.state.style;
      this.#target.write({ kind: "style", style: Style.None });
    }
    this.#target.write(NEWLINE);
    const wrapIndent = this.#target.state.wrapIndent;
    if (wrapIndent !== void 0) {
      this.#target.write({ kind: "breaking", str: "".padEnd(wrapIndent), width: wrapIndent });
    } else {
      this.#emitPrefix(this.#wrapPrefix);
    }
    if (style) {
      this.#target.write({ kind: "style", style });
    }
  }
  #enqueue(token) {
    if (this.#output) {
      this.#output.push(token);
    } else {
      this.#output = new ContiguousOutputSegment(token);
    }
  }
}
((Wrapper2) => {
  Wrapper2.prefixStart = "\x1B[<<~";
  Wrapper2.prefixStop = "\x1B[>>~";
})(Wrapper || (Wrapper = {}));
export {
  Wrapper
};
//# sourceMappingURL=wrapper.js.map
