import { lengthAnsi } from "./ansi.js";
import { split } from "./split.js";
class Line {
  n = 0;
  w = [];
  constructor(word, n) {
    word != null && this.add(word, n);
  }
  add(word, n = word.length) {
    this.w.push(word);
    this.n += n + ~~(this.n > 0);
    return this;
  }
  toString() {
    return this.w.join(" ");
  }
}
const SPLIT_PLAIN = {
  length: (x) => x.length,
  split: (_, max) => max
};
const SPLIT_ANSI = {
  length: lengthAnsi,
  split: (x, max) => {
    const re = /\x1b\[[0-9;]+m/g;
    let i = max;
    let match;
    while (match = re.exec(x)) {
      if (match.index >= max) break;
      const n = match[0].length;
      i += n;
      max += n;
    }
    return i;
  }
};
const __append = (acc, word, wordLen, width) => {
  const curr = acc[acc.length - 1];
  curr && width - curr.n > wordLen ? curr.add(word, wordLen) : acc.push(new Line(word, wordLen));
};
const __wrapWord = (word, { width, min, hard, splitter }, offset = 0, acc = []) => {
  let len = splitter.length(word);
  let free = width - offset;
  if (free < min && free < len) {
    free = width;
  }
  while (hard && len > free) {
    const split2 = splitter.split(word, free);
    const chunk = word.substring(0, split2);
    __append(acc, chunk, free, width);
    word = word.substring(split2);
    free = width;
    len = splitter.length(word);
  }
  __append(acc, word, len, width);
  return acc;
};
const wordWrapLine = (line, opts, acc = []) => {
  if (!line.length) {
    acc.push(new Line());
    return acc;
  }
  const $opts = {
    width: 80,
    min: 4,
    hard: false,
    splitter: SPLIT_PLAIN,
    ...opts
  };
  for (let word of split(line, opts.delimWord || /\s/g)) {
    const curr = acc[acc.length - 1];
    __wrapWord(word, $opts, curr && curr.n > 0 ? curr.n + 1 : 0, acc);
  }
  return acc;
};
const wordWrapLines = (lines, opts) => {
  let acc = [];
  for (let line of split(lines, opts.delimLine)) {
    acc = acc.concat(wordWrapLine(line, opts));
  }
  return acc;
};
const wordWrap = (str, opts) => wordWrapLines(str, opts).join("\n");
export {
  Line,
  SPLIT_ANSI,
  SPLIT_PLAIN,
  wordWrap,
  wordWrapLine,
  wordWrapLines
};
