import { keydownHandler } from "./../../tiptap/pm/keymap.js";
import { Fragment, Slice } from "./../../tiptap/pm/model.js";
import { Plugin, PluginKey, TextSelection } from "./../../tiptap/pm/state.js";
import { Decoration, DecorationSet } from "./../../tiptap/pm/view.js";
import { isInlineTextOnly } from "../transaction-predicates/is-inline-text-only.js";
import { onlyChangesUnrelatedAttrs } from "../transaction-predicates/only-changes-unrelated-attrs.js";
import { GapCursor } from "./gap-cursor-selection.js";
import { createGapSpacerElement } from "./gap-spacer-component/gap-spacer-html.js";
import styles from "./gap-spacer-component/gap-spacer-styles-scss.js";
import {
  isBetweenFloatedNodes,
  isBetweenFullWidthNodes,
  isCollapsedBetween,
  needsTopMarginForPmNode
} from "./gap-spacer-nodes.js";
const gapSpacerPluginKey = new PluginKey("gap-spacer");
const RELEVANT_ATTRS = /* @__PURE__ */ new Set(["containerData"]);
const getFocusedPos = (state) => state.selection instanceof GapCursor ? state.selection.head : null;
const collectGapPositions = (doc) => {
  const positions = [];
  const seen = /* @__PURE__ */ new Set();
  const addPosition = (pos) => {
    if (!seen.has(pos)) {
      seen.add(pos);
      positions.push(pos);
    }
  };
  doc.descendants((node, pos) => {
    addPosition(pos);
    addPosition(pos + node.nodeSize);
  });
  return positions;
};
const computeGapSpacerPositions = (doc) => {
  const positions = [];
  const candidatePositions = collectGapPositions(doc);
  for (const pos of candidatePositions) {
    const $pos = doc.resolve(pos);
    if (!GapCursor.valid($pos)) {
      continue;
    }
    const bothFullWidth = isBetweenFullWidthNodes($pos.nodeBefore, $pos.nodeAfter);
    const collapsed = isCollapsedBetween(
      $pos.nodeBefore?.type.name,
      $pos.nodeAfter?.type.name,
      $pos.parent?.type.name,
      { bothFullWidth }
    );
    const betweenFloated = isBetweenFloatedNodes($pos.nodeBefore, $pos.nodeAfter);
    const isFirstPosition = !$pos.nodeBefore && $pos.depth === 0;
    const hasTopOverflow = needsTopMarginForPmNode($pos.nodeAfter);
    positions.push({
      pos,
      collapsed,
      betweenFloated,
      needsTopMargin: isFirstPosition && hasTopOverflow
    });
  }
  return positions;
};
const gapSpacer = (t) => {
  return new Plugin({
    key: gapSpacerPluginKey,
    state: {
      init: (_, state) => {
        const doc = state.doc;
        const positions = computeGapSpacerPositions(doc);
        const focusedPos = getFocusedPos(state);
        return {
          positions,
          focusedPos,
          decorations: buildGapSpacerDecorationSet({
            doc,
            t,
            positions,
            focusedPos
          })
        };
      },
      apply: (tr, prev, _oldState, newState) => {
        const newDoc = newState.doc;
        const newFocusedPos = getFocusedPos(newState);
        let nextPositions = prev.positions;
        let positionsChanged = false;
        if (tr.docChanged) {
          if (isInlineTextOnly(tr)) {
            nextPositions = prev.positions.map((position) => ({
              ...position,
              pos: tr.mapping.map(position.pos)
            }));
            positionsChanged = true;
          } else if (!onlyChangesUnrelatedAttrs(tr, RELEVANT_ATTRS)) {
            nextPositions = computeGapSpacerPositions(newDoc);
            positionsChanged = true;
          }
        }
        if (!positionsChanged && prev.focusedPos === newFocusedPos) {
          return prev;
        }
        return {
          positions: nextPositions,
          focusedPos: newFocusedPos,
          decorations: buildGapSpacerDecorationSet({
            doc: newDoc,
            t,
            positions: nextPositions,
            focusedPos: newFocusedPos
          })
        };
      }
    },
    props: {
      decorations: (state) => {
        const pluginState = gapSpacerPluginKey.getState(state);
        return pluginState?.decorations ?? null;
      },
      handleKeyDown,
      handleDOMEvents: {
        beforeinput
      }
    }
  });
};
const arrow = (axis, dir) => {
  const direction = axis === "vertical" ? dir > 0 ? "down" : "up" : dir > 0 ? "right" : "left";
  return function(state, dispatch, view) {
    const sel = state.selection;
    let $start = dir > 0 ? sel.$to : sel.$from;
    let mustMove = sel.empty;
    if (sel instanceof TextSelection) {
      if (!view.endOfTextblock(direction) || $start.depth === 0) {
        return false;
      }
      mustMove = false;
      $start = state.doc.resolve(dir > 0 ? $start.after() : $start.before());
    }
    const $found = GapCursor.findGapCursorFrom($start, dir, mustMove);
    if (!$found) {
      return false;
    }
    if (dispatch) {
      dispatch(state.tr.setSelection(new GapCursor($found)));
    }
    return true;
  };
};
const handleKeyDown = keydownHandler({
  ArrowLeft: arrow("horizontal", -1),
  ArrowRight: arrow("horizontal", 1),
  ArrowUp: arrow("vertical", -1),
  ArrowDown: arrow("vertical", 1)
});
const beforeinput = (view, event) => {
  if (event.inputType !== "insertCompositionText" || !(view.state.selection instanceof GapCursor)) {
    return false;
  }
  const { $from } = view.state.selection;
  const insert = $from.parent.contentMatchAt($from.index()).findWrapping(view.state.schema.nodes.text);
  if (!insert) {
    return false;
  }
  let frag = Fragment.empty;
  for (let i = insert.length - 1; i >= 0; i--) {
    frag = Fragment.from(insert[i].createAndFill(null, frag));
  }
  const tr = view.state.tr.replace($from.pos, $from.pos, new Slice(frag, 0, 0));
  tr.setSelection(TextSelection.near(tr.doc.resolve($from.pos + 1)));
  view.dispatch(tr);
  return false;
};
const buildGapSpacerDecorationSet = ({
  doc,
  t,
  positions,
  focusedPos
}) => {
  if (positions.length === 0) {
    return null;
  }
  const decorations = positions.map(
    ({ pos, collapsed, betweenFloated, needsTopMargin: topMargin }) => createSpacer({
      pos,
      isFocused: focusedPos === pos,
      t,
      collapsed,
      betweenFloated,
      needsTopMargin: topMargin
    })
  );
  return DecorationSet.create(doc, decorations);
};
const createSpacer = ({
  pos,
  isFocused = false,
  t,
  collapsed,
  betweenFloated,
  needsTopMargin: topMargin = false
}) => Decoration.widget(
  pos,
  (view) => {
    const container = document.createElement("div");
    if (betweenFloated) {
      container.className = styles.betweenFloated;
    }
    const gapSpacerElement = createGapSpacerElement({
      collapsed,
      isFocused,
      tooltipContent: t("GapSpacer_Tooltip"),
      needsTopMargin: topMargin,
      onClick: (event) => {
        event?.preventDefault();
        event?.stopImmediatePropagation();
      },
      onMouseDown: (event) => {
        event?.preventDefault();
        event?.stopImmediatePropagation();
        const { state, dispatch } = view;
        const paragraph = state.schema.nodes.PARAGRAPH;
        const tr = state.tr.insert(pos, paragraph.create());
        tr.setSelection(TextSelection.create(tr.doc, pos, pos + 1));
        dispatch(tr);
      }
    });
    container.appendChild(gapSpacerElement);
    return container;
  },
  {
    key: `gapspacer-${pos}-${isFocused ? "focused" : "unfocused"}-${collapsed ? "collapsed" : "expanded"}-${betweenFloated ? "floated" : "static"}-${topMargin ? "topMargin" : "noTopMargin"}`
  }
);
export {
  createSpacer,
  gapSpacer
};
//# sourceMappingURL=gap-spacer-plugin.js.map
