// ets_tracing: off
import { Case } from "@effect-ts/core/Case";
import * as C from "@effect-ts/core/Collections/Immutable/Chunk";
import { constVoid, pipe } from "@effect-ts/core/Function";
import * as IO from "@effect-ts/core/IO";
import * as HorizontalMergeRules from "../HorizontalMergeRules/index.mjs";
import * as MA from "../MergeAction/index.mjs";
export class MergeState extends Case {}
/**
 * Represents the status of the merge loop.
 */

export class MergeLoopState extends Case {}
/**
 * Represents the status of the append loop.
 */

export class AppendLoopState extends Case {}
/**
 * Represents the three sections of a set of columns.
 */

export class Sections extends Case {} // -----------------------------------------------------------------------------
// Constructors
// -----------------------------------------------------------------------------

export function sections(left, overlap, right) {
  return new Sections({
    left,
    overlap,
    right
  });
}
export function appendLoopState(lastCharWidth = 0) {
  return new AppendLoopState({
    lastCharWidth
  });
}
export function mergeLoopState(params) {
  return new MergeLoopState({
    a: C.empty(),
    b: C.empty(),
    overlap: 0,
    partialResult: C.empty(),
    appendLoopState: appendLoopState(),
    ...params
  });
} // -----------------------------------------------------------------------------
// Operations
// -----------------------------------------------------------------------------

/**
 * Creates a merge strategy to perform horizontal merging, based on the given
 * `RenderOptions`.
 *
 * @param options The `RenderOptions` used to build the `MergeStrategy`.
 * @return A `MergeStrategy` function that performs horizontal merging.
 */

export function mergeStrategy(options) {
  return HorizontalMergeRules.mergeStrategy(options);
}
export function appendLoop(options, figures, partial, state) {
  return IO.run(appendLoopRec(options, figures, partial, state));
}

function appendLoopRec(options, figures, partial, state) {
  return IO.gen(function* (_) {
    if (C.isEmpty(figures)) {
      return partial;
    }

    const {
      tuple: [head, remainingChars]
    } = C.splitAt_(figures, 1);
    const figChar = C.unsafeGet_(head, 0);
    const {
      tuple: [upperLines, last]
    } = C.splitAt_(partial, partial.length - 1);
    const lastLine = C.unsafeGet_(last, 0);
    const merged = yield* _(mergeRec(options, mergeLoopState({
      a: lastLine,
      b: figChar,
      appendLoopState: state
    })));
    const onBorder = yield* _(mergeRec(options, mergeLoopState({
      b: figChar
    })));
    const result = merged.length <= options.maxWidth ? C.append_(upperLines, merged) : C.append_(partial, onBorder);
    const nextState = state.copy({
      lastCharWidth: figChar.length
    });
    return yield* _(appendLoopRec(options, remainingChars, result, nextState));
  });
}

function mergeRec(options, state) {
  return IO.gen(function* (_) {
    if (state.overlap === 0) {
      return yield* _(mergeRec(options, state.copy({
        overlap: 1,
        partialResult: C.concat_(state.a, state.b)
      })));
    }

    if (state.overlap > state.b.length) {
      return state.partialResult;
    }

    const mState = new MergeState({
      overlap: state.overlap,
      currentCharWidth: state.b.length,
      lastCharWidth: state.appendLoopState.lastCharWidth
    });
    const aLeftCut = Math.max(0, state.a.length - state.overlap);
    const aRightCut = Math.min(state.a.length, state.a.length - state.overlap + state.b.length);
    const aSections = splitSections(aLeftCut, aRightCut, state.a);
    const bLeftCut = Math.max(0, state.overlap - state.a.length);
    const bRightCut = Math.min(state.overlap, state.b.length);
    const bSections = splitSections(bLeftCut, bRightCut, state.b);
    const leftSide = mergeOnLeftBorder(options, mState, bSections.left);
    const merged = mergeOverlappingSections(options, mState, aSections.overlap, bSections.overlap);
    const rightSide = new MA.Continue({
      value: C.concat_(aSections.right, bSections.right)
    });
    const result = MA.map_(MA.tuple(leftSide, merged, rightSide), ([_, merged, right]) => C.concat_(aSections.left, C.concat_(merged, right)));

    switch (result._tag) {
      case "Stop":
        return state.partialResult;

      case "CurrentLast":
        return result.value;

      case "Continue":
        return yield* _(mergeRec(options, state.copy({
          overlap: state.overlap + 1,
          partialResult: result.value
        })));
    }
  });
}
/**
 * Divides a set of columns into 3 sections with cuts in 2 points.
 */


function splitSections(aPoint, bPoint, figure) {
  return C.reduce_(C.zipWithIndex(figure), sections(C.empty(), C.empty(), C.empty()), (store, {
    tuple: [column, i]
  }) => {
    if (i < aPoint) return store.copy({
      left: C.append_(store.left, column)
    });
    if (i >= bPoint) return store.copy({
      right: C.append_(store.right, column)
    });
    return store.copy({
      overlap: C.append_(store.overlap, column)
    });
  });
}
/**
 * Merges the two overlapping sections of two characters.
 */


function mergeOverlappingSections(options, state, aSection, bSection) {
  return C.forEachF(MA.Applicative)(({
    tuple: [aActiveColumn, bActiveColumn]
  }) => {
    const aColumnSplit = C.from(aActiveColumn.split(""));
    const bColumnSplit = C.from(bActiveColumn.split(""));
    return MA.map_(C.forEachF(MA.Applicative)(({
      tuple: [aChar, bChar]
    }) => mergeStrategy(options)(state)(aChar, bChar))(C.zip_(aColumnSplit, bColumnSplit)), C.join(""));
  })(C.zip_(aSection, bSection));
}
/**
 * Merges a character on the left border.
 */


function mergeOnLeftBorder(options, state, section) {
  return MA.map_(C.forEachF(MA.Applicative)(col => C.forEachF(MA.Applicative)(char => char === " " ? mergeStrategy(options)(state)(options.font.header.hardblank, " ") : new MA.Stop())(C.from(col.split(""))))(section), constVoid);
}
//# sourceMappingURL=index.mjs.map