import { __require as requireBlockMapBuilder } from './BlockMapBuilder.js';
import { __require as requireContentBlockNode } from './ContentBlockNode.js';
import { __require as requireImmutable } from '../../../../immutable@3.7.6/node_modules/immutable/dist/immutable.js';
import { __require as requireInsertIntoList } from './insertIntoList.js';
import { __require as requireInvariant } from '../../../../fbjs@2.0.0/node_modules/fbjs/lib/invariant.js';
import { __require as requireRandomizeBlockMapKeys } from './randomizeBlockMapKeys.js';

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @format
 * 
 * @emails oncall+draft_js
 */

var insertFragmentIntoContentState_1;
var hasRequiredInsertFragmentIntoContentState;

function requireInsertFragmentIntoContentState () {
	if (hasRequiredInsertFragmentIntoContentState) return insertFragmentIntoContentState_1;
	hasRequiredInsertFragmentIntoContentState = 1;

	var BlockMapBuilder = requireBlockMapBuilder();

	var ContentBlockNode = requireContentBlockNode();

	var Immutable = requireImmutable();

	var insertIntoList = requireInsertIntoList();

	var invariant = requireInvariant();

	var randomizeBlockMapKeys = requireRandomizeBlockMapKeys();

	var List = Immutable.List;

	var updateExistingBlock = function updateExistingBlock(contentState, selectionState, blockMap, fragmentBlock, targetKey, targetOffset) {
	  var mergeBlockData = arguments.length > 6 && arguments[6] !== undefined ? arguments[6] : 'REPLACE_WITH_NEW_DATA';
	  var targetBlock = blockMap.get(targetKey);
	  var text = targetBlock.getText();
	  var chars = targetBlock.getCharacterList();
	  var finalKey = targetKey;
	  var finalOffset = targetOffset + fragmentBlock.getText().length;
	  var data = null;

	  switch (mergeBlockData) {
	    case 'MERGE_OLD_DATA_TO_NEW_DATA':
	      data = fragmentBlock.getData().merge(targetBlock.getData());
	      break;

	    case 'REPLACE_WITH_NEW_DATA':
	      data = fragmentBlock.getData();
	      break;
	  }

	  var type = targetBlock.getType();

	  if (text && type === 'unstyled') {
	    type = fragmentBlock.getType();
	  }

	  var newBlock = targetBlock.merge({
	    text: text.slice(0, targetOffset) + fragmentBlock.getText() + text.slice(targetOffset),
	    characterList: insertIntoList(chars, fragmentBlock.getCharacterList(), targetOffset),
	    type: type,
	    data: data
	  });
	  return contentState.merge({
	    blockMap: blockMap.set(targetKey, newBlock),
	    selectionBefore: selectionState,
	    selectionAfter: selectionState.merge({
	      anchorKey: finalKey,
	      anchorOffset: finalOffset,
	      focusKey: finalKey,
	      focusOffset: finalOffset,
	      isBackward: false
	    })
	  });
	};
	/**
	 * Appends text/characterList from the fragment first block to
	 * target block.
	 */


	var updateHead = function updateHead(block, targetOffset, fragment) {
	  var text = block.getText();
	  var chars = block.getCharacterList(); // Modify head portion of block.

	  var headText = text.slice(0, targetOffset);
	  var headCharacters = chars.slice(0, targetOffset);
	  var appendToHead = fragment.first();
	  return block.merge({
	    text: headText + appendToHead.getText(),
	    characterList: headCharacters.concat(appendToHead.getCharacterList()),
	    type: headText ? block.getType() : appendToHead.getType(),
	    data: appendToHead.getData()
	  });
	};
	/**
	 * Appends offset text/characterList from the target block to the last
	 * fragment block.
	 */


	var updateTail = function updateTail(block, targetOffset, fragment) {
	  // Modify tail portion of block.
	  var text = block.getText();
	  var chars = block.getCharacterList(); // Modify head portion of block.

	  var blockSize = text.length;
	  var tailText = text.slice(targetOffset, blockSize);
	  var tailCharacters = chars.slice(targetOffset, blockSize);
	  var prependToTail = fragment.last();
	  return prependToTail.merge({
	    text: prependToTail.getText() + tailText,
	    characterList: prependToTail.getCharacterList().concat(tailCharacters),
	    data: prependToTail.getData()
	  });
	};

	var getRootBlocks = function getRootBlocks(block, blockMap) {
	  var headKey = block.getKey();
	  var rootBlock = block;
	  var rootBlocks = []; // sometimes the fragment head block will not be part of the blockMap itself this can happen when
	  // the fragment head is used to update the target block, however when this does not happen we need
	  // to make sure that we include it on the rootBlocks since the first block of a fragment is always a
	  // fragment root block

	  if (blockMap.get(headKey)) {
	    rootBlocks.push(headKey);
	  }

	  while (rootBlock && rootBlock.getNextSiblingKey()) {
	    var lastSiblingKey = rootBlock.getNextSiblingKey();

	    if (!lastSiblingKey) {
	      break;
	    }

	    rootBlocks.push(lastSiblingKey);
	    rootBlock = blockMap.get(lastSiblingKey);
	  }

	  return rootBlocks;
	};

	var updateBlockMapLinks = function updateBlockMapLinks(blockMap, originalBlockMap, targetBlock, fragmentHeadBlock) {
	  return blockMap.withMutations(function (blockMapState) {
	    var targetKey = targetBlock.getKey();
	    var headKey = fragmentHeadBlock.getKey();
	    var targetNextKey = targetBlock.getNextSiblingKey();
	    var targetParentKey = targetBlock.getParentKey();
	    var fragmentRootBlocks = getRootBlocks(fragmentHeadBlock, blockMap);
	    var lastRootFragmentBlockKey = fragmentRootBlocks[fragmentRootBlocks.length - 1];

	    if (blockMapState.get(headKey)) {
	      // update the fragment head when it is part of the blockMap otherwise
	      blockMapState.setIn([targetKey, 'nextSibling'], headKey);
	      blockMapState.setIn([headKey, 'prevSibling'], targetKey);
	    } else {
	      // update the target block that had the fragment head contents merged into it
	      blockMapState.setIn([targetKey, 'nextSibling'], fragmentHeadBlock.getNextSiblingKey());
	      blockMapState.setIn([fragmentHeadBlock.getNextSiblingKey(), 'prevSibling'], targetKey);
	    } // update the last root block fragment


	    blockMapState.setIn([lastRootFragmentBlockKey, 'nextSibling'], targetNextKey); // update the original target next block

	    if (targetNextKey) {
	      blockMapState.setIn([targetNextKey, 'prevSibling'], lastRootFragmentBlockKey);
	    } // update fragment parent links


	    fragmentRootBlocks.forEach(function (blockKey) {
	      return blockMapState.setIn([blockKey, 'parent'], targetParentKey);
	    }); // update targetBlock parent child links

	    if (targetParentKey) {
	      var targetParent = blockMap.get(targetParentKey);
	      var originalTargetParentChildKeys = targetParent.getChildKeys();
	      var targetBlockIndex = originalTargetParentChildKeys.indexOf(targetKey);
	      var insertionIndex = targetBlockIndex + 1;
	      var newChildrenKeysArray = originalTargetParentChildKeys.toArray(); // insert fragment children

	      newChildrenKeysArray.splice.apply(newChildrenKeysArray, [insertionIndex, 0].concat(fragmentRootBlocks));
	      blockMapState.setIn([targetParentKey, 'children'], List(newChildrenKeysArray));
	    }
	  });
	};

	var insertFragment = function insertFragment(contentState, selectionState, blockMap, fragment, targetKey, targetOffset) {
	  var isTreeBasedBlockMap = blockMap.first() instanceof ContentBlockNode;
	  var newBlockArr = [];
	  var fragmentSize = fragment.size;
	  var target = blockMap.get(targetKey);
	  var head = fragment.first();
	  var tail = fragment.last();
	  var finalOffset = tail.getLength();
	  var finalKey = tail.getKey();
	  var shouldNotUpdateFromFragmentBlock = isTreeBasedBlockMap && (!target.getChildKeys().isEmpty() || !head.getChildKeys().isEmpty());
	  blockMap.forEach(function (block, blockKey) {
	    if (blockKey !== targetKey) {
	      newBlockArr.push(block);
	      return;
	    }

	    if (shouldNotUpdateFromFragmentBlock) {
	      newBlockArr.push(block);
	    } else {
	      newBlockArr.push(updateHead(block, targetOffset, fragment));
	    } // Insert fragment blocks after the head and before the tail.


	    fragment // when we are updating the target block with the head fragment block we skip the first fragment
	    // head since its contents have already been merged with the target block otherwise we include
	    // the whole fragment
	    .slice(shouldNotUpdateFromFragmentBlock ? 0 : 1, fragmentSize - 1).forEach(function (fragmentBlock) {
	      return newBlockArr.push(fragmentBlock);
	    }); // update tail

	    newBlockArr.push(updateTail(block, targetOffset, fragment));
	  });
	  var updatedBlockMap = BlockMapBuilder.createFromArray(newBlockArr);

	  if (isTreeBasedBlockMap) {
	    updatedBlockMap = updateBlockMapLinks(updatedBlockMap, blockMap, target, head);
	  }

	  return contentState.merge({
	    blockMap: updatedBlockMap,
	    selectionBefore: selectionState,
	    selectionAfter: selectionState.merge({
	      anchorKey: finalKey,
	      anchorOffset: finalOffset,
	      focusKey: finalKey,
	      focusOffset: finalOffset,
	      isBackward: false
	    })
	  });
	};

	var insertFragmentIntoContentState = function insertFragmentIntoContentState(contentState, selectionState, fragmentBlockMap) {
	  var mergeBlockData = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 'REPLACE_WITH_NEW_DATA';
	  !selectionState.isCollapsed() ? process.env.NODE_ENV !== "production" ? invariant(false, '`insertFragment` should only be called with a collapsed selection state.') : invariant(false) : void 0;
	  var blockMap = contentState.getBlockMap();
	  var fragment = randomizeBlockMapKeys(fragmentBlockMap);
	  var targetKey = selectionState.getStartKey();
	  var targetOffset = selectionState.getStartOffset();
	  var targetBlock = blockMap.get(targetKey);

	  if (targetBlock instanceof ContentBlockNode) {
	    !targetBlock.getChildKeys().isEmpty() ? process.env.NODE_ENV !== "production" ? invariant(false, '`insertFragment` should not be called when a container node is selected.') : invariant(false) : void 0;
	  } // When we insert a fragment with a single block we simply update the target block
	  // with the contents of the inserted fragment block


	  if (fragment.size === 1) {
	    return updateExistingBlock(contentState, selectionState, blockMap, fragment.first(), targetKey, targetOffset, mergeBlockData);
	  }

	  return insertFragment(contentState, selectionState, blockMap, fragment, targetKey, targetOffset);
	};

	insertFragmentIntoContentState_1 = insertFragmentIntoContentState;
	return insertFragmentIntoContentState_1;
}

export { requireInsertFragmentIntoContentState as __require };
//# sourceMappingURL=insertFragmentIntoContentState.js.map
