import { JSONTransformer } from '@atlaskit/editor-json-transformer';
import { findParentNodeOfType } from '@atlaskit/editor-prosemirror/utils';
/**
 * Finds all top level nodes affected by the transaction
 * Uses from/to positions in transaction's steps to work out which nodes will
 * be changed by the transaction
 */
export var findChangedNodesFromTransaction = function findChangedNodesFromTransaction(tr) {
  var nodes = [];
  var steps = tr.steps || [];
  steps.forEach(function (step) {
    step.getMap().forEach(function (_oldStart, _oldEnd, newStart, newEnd) {
      tr.doc.nodesBetween(newStart, Math.min(newEnd, tr.doc.content.size), function (node) {
        if (!nodes.find(function (n) {
          return n === node;
        })) {
          nodes.push(node);
        }
        return false;
      });
    });
  });
  return nodes;
};
export var validNode = function validNode(node) {
  try {
    node.check(); // this will throw an error if the node is invalid
  } catch (error) {
    return false;
  }
  return true;
};

/** Validates prosemirror nodes, and returns true only if all nodes are valid */
export var validateNodes = function validateNodes(nodes) {
  return nodes.every(validNode);
};
export var isType = function isType(node, type) {
  return type && node && node.type === type;
};
export var isParagraph = function isParagraph(node, schema) {
  return isType(node, schema.nodes.paragraph);
};
export var isText = function isText(node, schema) {
  return isType(node, schema.nodes.text);
};
export var isLinkMark = function isLinkMark(node, schema) {
  return isType(node, schema.marks.link);
};
export var SelectedState = /*#__PURE__*/function (SelectedState) {
  SelectedState[SelectedState["selectedInRange"] = 0] = "selectedInRange";
  SelectedState[SelectedState["selectedInside"] = 1] = "selectedInside";
  return SelectedState;
}({});

/**
 * Returns if the current selection from achor-head is selecting the node.
 * If the node is not selected then null is returned.
 * If the node is selected then an enum is returned that describes weather the node
 * is fully selected by a range or if the "inside" of the node has been selected or clicked.
 */
export var isNodeSelectedOrInRange = function isNodeSelectedOrInRange(anchorPosition, headPosition, nodePosition, nodeSize) {
  if (typeof nodePosition !== 'number') {
    return null;
  }
  var rangeStart = Math.min(anchorPosition, headPosition);
  var rangeEnd = Math.max(anchorPosition, headPosition);
  var nodeStart = nodePosition;
  var nodeEnd = nodePosition + nodeSize;
  if (anchorPosition === headPosition) {
    return null;
  }
  if (rangeStart <= nodeStart && nodeEnd < rangeEnd || rangeStart < nodeStart && nodeEnd <= rangeEnd) {
    return SelectedState.selectedInRange;
  }
  if (nodeStart <= anchorPosition && headPosition <= nodeEnd) {
    return SelectedState.selectedInside;
  }
  return null;
};

/**
 * Checks if a particular node fragment is supported in the parent
 * @param state EditorState
 * @param fragment The fragment to be checked for
 */
export var isSupportedInParent = function isSupportedInParent(state, fragment, currentAppearance) {
  var depth = currentAppearance === 'embed' || currentAppearance === 'block' ? undefined : -1;
  var parent = state.selection.$from.node(depth);
  return parent && parent.type.validContent(fragment);
};

/**
 * Checks if the passed in node is a media node
 * Includes media, mediaInline, mediaGroup, mediaSingle
 * @param node The PM node to be checked
 */
export var isMediaNode = function isMediaNode(node) {
  return ['media', 'mediaInline', 'mediaGroup', 'mediaSingle'].includes(node.type.name);
};

/**
 * Checks if the node before selection is a media node
 * If there is no node before, checks the node before the parent node
 * Includes media, mediaInline, mediaGroup, mediaSingle
 * @param $pos The position of the selection
 * @param state The editor state
 */
export var isNodeBeforeMediaNode = function isNodeBeforeMediaNode($pos, state) {
  var nodeBefore = $pos.nodeBefore;
  if (!nodeBefore) {
    var depthOfParent = $pos.depth - 1 || 1;
    var parentNode = findParentNodeOfType([state.schema.nodes["".concat($pos.node(depthOfParent).type.name)]])(state.selection);
    var resolvedPosOfParentNode = parentNode ? state.tr.doc.resolve(parentNode.pos) : undefined;
    var nodeBeforeParent = resolvedPosOfParentNode && resolvedPosOfParentNode.pos < state.doc.nodeSize ? resolvedPosOfParentNode.nodeBefore : undefined;
    if (nodeBeforeParent) {
      nodeBefore = nodeBeforeParent;
    }
  }
  if (nodeBefore) {
    return ['media', 'mediaInline', 'mediaGroup', 'mediaSingle'].includes(nodeBefore.type.name);
  }
  return false;
};
var transformer = new JSONTransformer();
export function toJSON(node) {
  return transformer.encode(node);
}
export function nodeToJSON(node) {
  return transformer.encodeNode(node);
}