'use strict';

var _toConsumableArray = require('@babel/runtime/helpers/toConsumableArray');
require('core-js/modules/es.array.concat.js');
require('core-js/modules/es.array.filter.js');
require('core-js/modules/es.array.find.js');
require('core-js/modules/es.array.includes.js');
require('core-js/modules/es.array.map.js');
require('core-js/modules/es.object.to-string.js');
require('core-js/modules/es.string.includes.js');
require('core-js/modules/web.dom-collections.for-each.js');
var React = require('react');
var useDNDProviderElement = require('../hooks/useDNDProviderElement.js');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var _toConsumableArray__default = /*#__PURE__*/_interopDefault(_toConsumableArray);
var React__default = /*#__PURE__*/_interopDefault(React);

var DropLocation = {
  Above: 'above',
  Below: 'below',
  Nested: 'nested'
};
var EditingStyle = {
  Single: 'single',
  Multiple: 'multiple',
  SingleNesting: 'single-nesting',
  MultipleNesting: 'multiple-nesting'
};

/**
 * Returns true if a passed in editing style is multiple rather than single
 * @param {EditingStyle} editingStyle current editing style
 */
var editingStyleIsMultiple = function editingStyleIsMultiple(editingStyle) {
  return editingStyle === EditingStyle.MultipleNesting || editingStyle === EditingStyle.Multiple;
};

/**
 * Returns a set of items that match the set of ids
 * @param {Array<ListItem>} items full set of items
 * @param {Array[string]} ids selected ids from the set of items
 */
var searchDraggedItem = function searchDraggedItem(items, ids) {
  var draggedItems = [];
  items.forEach(function (item) {
    if (ids.some(function (id) {
      return item.id === id;
    })) {
      draggedItems.push(item);
    } else if (item.children) {
      draggedItems = [].concat(_toConsumableArray__default.default(draggedItems), _toConsumableArray__default.default(searchDraggedItem(item.children, ids)));
    }
  });
  return draggedItems;
};

/**
 * Helper function that returns a new list with the dragged items moved to the specified location
 * @param {Array<ListItem>} items original list of ListItems
 * @param {Array<ListItem>} draggedItems items to be moved
 * @param {string} dropId the id of the drop location
 * @param {DropLocation} location Where the drop will occur, above or below the specified id or as the first child of the specified id
 * @param {boolean} dropped flag stating if the drop has already occured in case there are duplicate ids in the list
 */
var updateList = function updateList(items, draggedItems, dropId, location) {
  var _draggedItems$;
  var dropped = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
  var finalList = [];
  var itemDropped = dropped; // Protects against dupe ids and multiple drops

  if (items && !((_draggedItems$ = draggedItems[0]) !== null && _draggedItems$ !== void 0 && (_draggedItems$ = _draggedItems$.children) !== null && _draggedItems$ !== void 0 && _draggedItems$.some(function (x) {
    return x.id === dropId;
  }))) {
    items.forEach(function (item) {
      var _children;
      // Insert dragged items in before location
      if (!itemDropped && item.id === dropId && location === DropLocation.Above) {
        finalList = [].concat(_toConsumableArray__default.default(finalList), _toConsumableArray__default.default(draggedItems));
        itemDropped = true;
      }
      var _item$children = item.children,
        children = _item$children === void 0 ? [] : _item$children;

      // Insert dragged items in nested location
      if (!itemDropped && location === DropLocation.Nested && dropId === item.id) {
        itemDropped = true;
        children = draggedItems.concat(updateList(children, draggedItems, dropId, location, itemDropped));
      } else if ((_children = children) !== null && _children !== void 0 && _children.length) {
        children = updateList(children, draggedItems, dropId, location, itemDropped);
      }

      // Add item into final list if it isn't a dragged item
      if (!draggedItems.find(function (drag) {
        return drag.id === item.id;
      })) {
        item.children = children; // eslint-disable-line no-param-reassign
        finalList.push(item);
      }

      // Insert dragged items in after location
      if (!itemDropped && item.id === dropId && location === DropLocation.Below) {
        finalList = [].concat(_toConsumableArray__default.default(finalList), _toConsumableArray__default.default(draggedItems));
      }
    });
    return finalList;
  }
  return items;
};

/**
 * Returns a new list with the dragged items moved to the specified location
 * @param {Array<ListItem>} items original list of ListItems
 * @param {Array<ListItem>} draggedItems items to be moved
 * @param {string} dropId the id of the drop location
 * @param {DropLocation} location Where the drop will occur, above or below the specified id or as the first child of the specified id
 */
var moveItemsInList = function moveItemsInList(items, dragIds, dropId, location) {
  var draggedItems = searchDraggedItem(items, dragIds.filter(function (id) {
    return id !== dropId;
  }));
  return updateList(items, draggedItems, dropId, location);
};

/**
 * Returns every child id from the ListItem and their children's ids
 * @param {ListItem} listItem
 */
var getAllChildIds = function getAllChildIds(listItem) {
  var lockedIds = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
  var childIds = [];
  if (listItem.children) {
    listItem.children.forEach(function (child) {
      if (!lockedIds.includes(child.id)) {
        childIds.push(child.id);
      }
      childIds = [].concat(_toConsumableArray__default.default(childIds), _toConsumableArray__default.default(getAllChildIds(child)));
    });
  }
  return childIds;
};

/**
 * Handles adding and removing items to a list to determine which list items are selected or deselected
 * @param {Array<ListItem>} list original list of ListItems
 * @param {Array<string>} currentSelection current selection of ListItems as ids
 * @param {string} id the id of an item that has been selected or deselected
 * @param {string} parentId the parent id of the selected or deselected item - if unselected, the parent is also unselected
 */
var handleEditModeSelect = function handleEditModeSelect(list, currentSelection, id, parentId, lockedIds) {
  var newSelection = [];
  if (currentSelection.filter(function (editId) {
    return editId === id;
  }).length > 0) {
    var _ref, _selectedItem$childre;
    var selectedItem = list.find(function (item) {
      return item.id === id;
    });
    var hasChildren = (_ref = (selectedItem === null || selectedItem === void 0 || (_selectedItem$childre = selectedItem.children) === null || _selectedItem$childre === void 0 ? void 0 : _selectedItem$childre.length) > 0) !== null && _ref !== void 0 ? _ref : false;
    var selectedItemChildrenIds = hasChildren ? selectedItem.children.map(function (child) {
      return child.id;
    }) : [];
    newSelection = currentSelection.filter(function (selected) {
      var notCurrentOrParent = selected !== id && selected !== parentId;
      return hasChildren ? !selectedItemChildrenIds.includes(selected) && notCurrentOrParent : notCurrentOrParent;
    });
  } else {
    list.forEach(function (editItem) {
      if (editItem.id === id) {
        newSelection.push(id);
        newSelection = [].concat(_toConsumableArray__default.default(newSelection), _toConsumableArray__default.default(getAllChildIds(editItem, lockedIds)));
      } else if (editItem.children) {
        newSelection = [].concat(_toConsumableArray__default.default(newSelection), _toConsumableArray__default.default(handleEditModeSelect(editItem.children, currentSelection, id, parentId, lockedIds)));
      }
      if (currentSelection.some(function (selectionId) {
        return selectionId === editItem.id;
      })) {
        newSelection.push(editItem.id);
      }

      // if there is a parentId, list is simply the current items siblings here, because of the
      // recursive call if the else if above.
      if (parentId && list.every(function (sibling) {
        return newSelection.includes(sibling.id);
      })) {
        newSelection.push(parentId);
      }
    });
  }
  return newSelection;
};

/**
 * checks if the given child id is selected.
 *
 * @param {array} selectedIds an array of selected ids
 * @returns function used in some/every calls in handleEditModeIndeterminateIds below
 */
var childIsSelected = function childIsSelected(selectedIds) {
  return function (childId) {
    return selectedIds.includes(childId);
  };
};

/**
 *
 * @param {array} list aray of list objects
 * @param {array} editModeSelectedIds an array of the selected ids
 * @returns an array of ids of the indeterminate rows
 */
var handleEditModeIndeterminateIds = function handleEditModeIndeterminateIds(list, editModeSelectedIds) {
  var indeterminateIds = [];
  if (!editModeSelectedIds.length) {
    return indeterminateIds;
  }
  list.forEach(function (item) {
    var childIds = getAllChildIds(item);
    var allSelected = childIds.every(childIsSelected(editModeSelectedIds));
    if (!allSelected && childIds.some(childIsSelected(editModeSelectedIds))) {
      indeterminateIds = [].concat(_toConsumableArray__default.default(indeterminateIds), [item.id]);
    }
  });
  return indeterminateIds;
};
function DragAndDrop(props) {
  var DNDElement = useDNDProviderElement.useDNDProviderElement(props);
  return /*#__PURE__*/React__default.default.createElement(React__default.default.Fragment, null, DNDElement);
}

/**
 * Allows the merging of multiple refs when a components needs to handle more than one,
 * e.g. when combining a map and a (drag and) drop target reference for the same element.
 * @param  {...any} refs Refs that should be merged are passed as separate parameters
 * @returns A function that will update the current property or call the ref function of all merged refs
 */
var mergeRefs = function mergeRefs() {
  for (var _len = arguments.length, refs = new Array(_len), _key = 0; _key < _len; _key++) {
    refs[_key] = arguments[_key];
  }
  var filteredRefs = refs.filter(Boolean);
  return !filteredRefs.length ? null : filteredRefs.length === 1 ? filteredRefs[0] : function (currentInstance) {
    filteredRefs.forEach(function (ref) {
      if (typeof ref === 'function') {
        ref(currentInstance);
      } else if (ref) {
        ref.current = currentInstance; // eslint-disable-line no-param-reassign
      }
    });
  };
};
DragAndDrop.__docgenInfo = {
  "description": "",
  "methods": [],
  "displayName": "DragAndDrop"
};

exports.DragAndDrop = DragAndDrop;
exports.DropLocation = DropLocation;
exports.EditingStyle = EditingStyle;
exports.editingStyleIsMultiple = editingStyleIsMultiple;
exports.handleEditModeIndeterminateIds = handleEditModeIndeterminateIds;
exports.handleEditModeSelect = handleEditModeSelect;
exports.mergeRefs = mergeRefs;
exports.moveItemsInList = moveItemsInList;
