import _extends from '@babel/runtime/helpers/extends';
import _toConsumableArray from '@babel/runtime/helpers/toConsumableArray';
import _slicedToArray from '@babel/runtime/helpers/slicedToArray';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import isEmpty from '../../node_modules/lodash-es/isEmpty.js';
import _omit from '../../node_modules/lodash-es/omit.js';
import 'core-js/modules/es.symbol.js';
import 'core-js/modules/es.array.concat.js';
import 'core-js/modules/es.array.filter.js';
import 'core-js/modules/es.array.find.js';
import 'core-js/modules/es.array.iterator.js';
import 'core-js/modules/es.array.map.js';
import 'core-js/modules/es.object.get-own-property-descriptor.js';
import 'core-js/modules/es.object.get-own-property-descriptors.js';
import 'core-js/modules/es.object.keys.js';
import 'core-js/modules/es.object.to-string.js';
import 'core-js/modules/web.dom-collections.for-each.js';
import 'core-js/modules/web.dom-collections.iterator.js';
import React__default, { useState, useRef, useMemo, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import { InlineLoading } from '@carbon/react';
import warning from 'warning';
import { findMatchingThresholds } from '../../utils/cardUtilityFunctions.js';
import { settings } from '../../constants/Settings.js';
import { HotspotPropTypes, HotspotIconPropType } from '../../constants/SharedPropTypes.js';
import { keyboardKeys } from '../../constants/KeyCodeConstants.js';
import Hotspot from './Hotspot.js';
import ImageControls from './ImageControls.js';
import HotspotContent from './HotspotContent.js';

function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var iotPrefix = settings.iotPrefix;
var propTypes = {
  /** id of the local image file to display */
  id: PropTypes.string,
  /** source of the local image file to display */
  src: PropTypes.string,
  /** alt tag and shown on mouseover */
  alt: PropTypes.string,
  /** value for object-fit property - 'contain' or 'fill' */
  displayOption: PropTypes.string,
  /** optional array of hotspots to render over the image */
  hotspots: PropTypes.arrayOf(PropTypes.shape(HotspotPropTypes)),
  /** optional features to enable or disable */
  hideZoomControls: PropTypes.bool,
  hideHotspots: PropTypes.bool,
  /* deprecated in favor of minimapBehavior below */
  // eslint-disable-next-line consistent-return
  hideMinimap: function hideMinimap(props, propName, componentName) {
    {
      var value = props[propName];
      /* istanbul ignore else */
      if (typeof value !== 'undefined') {
        return new Error("".concat(componentName, ": '").concat(propName, "' prop is deprecated in favor of 'minimapBehavior=\"").concat(value ? 'hide' : 'show', "\"'."));
      }
    }
  },
  /* 'hide' always hides the minimap, 'show' always shows the minimap, 'showOnPan' only shows the minimap when panning an image */
  minimapBehavior: PropTypes.oneOf(['hide', 'show', 'showOnPan']),
  /** when true activates mouse event based create & select hotspot fuctionality */
  isEditable: PropTypes.bool,
  isHotspotDataLoading: PropTypes.bool,
  /** Background color to display around the image */
  background: PropTypes.string,
  /** Current height in pixels */
  height: PropTypes.number.isRequired,
  /**
   * Array of identifiable icons used by the hotspots. The icons here will be used to
   * render the hotspot icons unless the renderIconByName prop us used.
   */
  icons: PropTypes.arrayOf(HotspotIconPropType),
  /**
   * Callback when an editable image is clicked without drag, used to add hotspot.
   * Emits position obj {x, y} of hotspot to be added.
   */
  onAddHotspotPosition: PropTypes.func,
  /** Callback when a hotspot is dragged to new position in isEditable mode
   * Emits new hotspots and updated position obj {x, y} of hotspot.
   */
  onUpdateHotspotPosition: PropTypes.func,
  /** Callback when a hotspot is clicked in isEditable mode, emits position obj {x, y} */
  onSelectHotspot: PropTypes.func,
  /**
   * Callback when the hotspot content is changed, emits position obj {x, y}
   * and content change obj, e.g. {title: 'new title value'}
   * */
  onHotspotContentChanged: PropTypes.func,
  /** Current width in pixels */
  width: PropTypes.number.isRequired,
  zoomMax: PropTypes.number,
  renderIconByName: PropTypes.func,
  i18n: PropTypes.shape({
    zoomIn: PropTypes.string,
    zoomOut: PropTypes.string,
    zoomToFit: PropTypes.string,
    titlePlaceholderText: PropTypes.string,
    titleEditableHintText: PropTypes.string
  }),
  /** locale string to pass for formatting */
  locale: PropTypes.string,
  /** The (unique) positions of the currently selected hotspots */
  selectedHotspots: PropTypes.arrayOf(PropTypes.shape({
    x: PropTypes.number,
    y: PropTypes.number
  }))
};
var defaultProps = {
  id: null,
  src: null,
  displayOption: null,
  hotspots: [],
  alt: null,
  hideZoomControls: false,
  hideHotspots: false,
  /* @deprecated in favor of minimapBehavior */
  hideMinimap: undefined,
  minimapBehavior: 'showOnPan',
  isHotspotDataLoading: false,
  isEditable: false,
  onAddHotspotPosition: function onAddHotspotPosition() {},
  onUpdateHotspotPosition: function onUpdateHotspotPosition() {},
  onSelectHotspot: function onSelectHotspot() {},
  onHotspotContentChanged: function onHotspotContentChanged() {},
  background: '#eee',
  zoomMax: undefined,
  renderIconByName: null,
  i18n: {
    zoomIn: 'Zoom in',
    zoomOut: 'Zoom out',
    zoomToFit: 'Zoom to fit',
    titlePlaceholderText: 'Enter label',
    titleEditableHintText: 'Click to edit label'
  },
  // undefined instead of null allows for functions to set default values
  locale: undefined,
  selectedHotspots: [],
  icons: null
};
var prepareDrag = function prepareDrag(event, cursor, setCursor) {
  setCursor(_objectSpread(_objectSpread({}, cursor), {}, {
    dragging: false,
    dragPrepared: true,
    imageMousedown: true
  }));
  event.preventDefault();
};
var startDrag = function startDrag(event, element, cursor, setCursor) {
  var cursorX = event.clientX;
  var cursorY = event.clientY;
  /* istanbul ignore else */
  if (element === 'image') {
    setCursor(_objectSpread(_objectSpread({}, cursor), {}, {
      cursorX: cursorX,
      cursorY: cursorY,
      dragging: true,
      dragPrepared: false
    }));
  }
  event.preventDefault();
};

/** update the image offset based on the dragged point, and the minimap on the relative opposite from the dragged point */
var whileDrag = function whileDrag(event, cursor, setCursor, image, setImage, minimap, setMinimap) {
  var cursorX = event.clientX;
  var cursorY = event.clientY;
  var deltaX = cursorX - cursor.cursorX;
  var deltaY = cursorY - cursor.cursorY;
  var newOffsetX = image.offsetX + deltaX;
  var newOffsetY = image.offsetY + deltaY;
  setCursor(_objectSpread(_objectSpread({}, cursor), {}, {
    cursorX: cursorX,
    cursorY: cursorY
  }));
  setImage(_objectSpread(_objectSpread({}, image), {}, {
    offsetX: newOffsetX,
    offsetY: newOffsetY
  }));
  setMinimap(_objectSpread(_objectSpread({}, minimap), {}, {
    offsetX: -(minimap.width / image.width * newOffsetX),
    offsetY: -(minimap.height / image.height * newOffsetY)
  }));
};

/** Make sure that the pointer hasn't left the */
var stopDrag = function stopDrag(cursor, setCursor) {
  setCursor(_objectSpread(_objectSpread({}, cursor), {}, {
    dragging: false,
    imageMousedown: false
  }));
};
var calculateImageWidth = function calculateImageWidth(_ref) {
  var container = _ref.container,
    orientation = _ref.orientation,
    ratio = _ref.ratio,
    _ref$scale = _ref.scale,
    scale = _ref$scale === void 0 ? 1 : _ref$scale,
    displayOption = _ref.displayOption;
  if (displayOption === 'fill') {
    return container.width * scale;
  }
  return (container.orientation === orientation ? orientation === 'landscape' ? ratio >= container.ratio ? container.width // landscape image bigger than landscape container
  : container.height * ratio // landscape image smaller than landscape container
  : ratio >= container.ratio ? container.height / ratio // portrait image bigger than portrait container
  : container.width // portrait image smaller than portrait container
  : orientation === 'landscape' ? container.width // landscape image and portrait container
  : container.height / ratio) * scale // portrait image and landscape container
  ;
};
var calculateImageHeight = function calculateImageHeight(_ref2) {
  var container = _ref2.container,
    orientation = _ref2.orientation,
    ratio = _ref2.ratio,
    _ref2$scale = _ref2.scale,
    scale = _ref2$scale === void 0 ? 1 : _ref2$scale,
    displayOption = _ref2.displayOption;
  if (displayOption === 'fill') {
    return container.height * scale;
  }
  return (container.orientation === orientation ? orientation === 'landscape' ? ratio >= container.ratio ? container.width / ratio // landscape image bigger than landscape container
  : container.height // landscape image smaller than landscape container
  : ratio >= container.ratio ? container.height // portrait image bigger than portrait container
  : container.width * ratio // portrait image smaller than portrait container
  : orientation === 'landscape' ? container.width / ratio // landscape image and portrait container
  : container.height) * scale // portrait image and landscape container
  ;
};
var calculateHotspotContainerLayout = function calculateHotspotContainerLayout(_ref3, _ref4, objectFit) {
  var imageScale = _ref3.scale,
    imageOrientation = _ref3.orientation,
    imageRatio = _ref3.ratio,
    imageOffsetY = _ref3.offsetY,
    imageObjectFitOffsetY = _ref3.objectFitOffsetY,
    imageWidth = _ref3.width,
    imageHeight = _ref3.height;
  var containerHeight = _ref4.height,
    containerWidth = _ref4.width;
  var width;
  var height;
  var top;
  var left;

  // CONTAIN
  if (objectFit === 'contain') {
    if (imageOrientation === 'landscape') {
      width = imageWidth;
      height = imageWidth / imageRatio;
      top = imageScale > 1 ? imageOffsetY : imageObjectFitOffsetY;
      left = imageScale > 1 ? 0 : (containerWidth - imageWidth) / 2;
    } else if (imageOrientation === 'portrait') {
      width = imageHeight / imageRatio;
      height = imageHeight;
      top = imageOffsetY;
      left = (containerWidth - width) / 2;
    }
    // FILL
  } else if (objectFit === 'fill') {
    width = imageScale > 1 ? imageWidth : containerWidth;
    height = imageScale > 1 ? imageHeight : containerHeight;
    top = imageOffsetY;
    left = 0;
    // NO OBJECT FIT
  } else if (!objectFit) {
    if (imageOrientation === 'landscape') {
      width = imageWidth;
      height = imageWidth / imageRatio;
      top = imageOffsetY;
      left = 0;
    } else if (imageOrientation === 'portrait') {
      width = imageHeight / imageRatio;
      height = imageHeight;
      top = imageOffsetY;
      left = (containerWidth - width) / 2;
    }
  }
  return {
    width: width,
    height: height,
    top: top,
    left: left
  };
};
var calculateObjectFitOffset = function calculateObjectFitOffset(_ref5) {
  var displayOption = _ref5.displayOption,
    container = _ref5.container,
    image = _ref5.image;
  var result = {
    x: 0,
    y: 0
  };
  if (displayOption === 'contain') {
    result.y = container.height / 2 - image.height / 2;
    result.x = container.width / 2 - image.width / 2;
  }
  return result;
};

/** Sets initialWidth and initialHeight of an image, offsets orientations in the state */
var onImageLoad = function onImageLoad(_ref6, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption) {
  var imageLoaded = _ref6.target;
  var initialWidth = imageLoaded.offsetWidth,
    initialHeight = imageLoaded.offsetHeight;
  var orientation = initialWidth > initialHeight ? 'landscape' : 'portrait';
  var ratio = orientation === 'landscape' ? initialWidth / initialHeight : initialHeight / initialWidth;
  var width = calculateImageWidth({
    container: container,
    orientation: orientation,
    ratio: ratio,
    displayOption: displayOption
  });
  var height = calculateImageHeight({
    container: container,
    orientation: orientation,
    ratio: ratio,
    displayOption: displayOption
  });

  // Because the first zoom is double, the initial image has to be big enough to support
  var resizable = initialWidth > 2 * width || initialHeight > 2 * height;
  setImage(_objectSpread(_objectSpread({}, image), {}, {
    initialWidth: initialWidth,
    initialHeight: initialHeight,
    width: width,
    height: height,
    scale: 1,
    ratio: ratio,
    orientation: orientation,
    offsetX: 0,
    offsetY: 0,
    objectFitOffsetY: 0,
    objectFitOffsetX: 0
  }));
  setMinimap(_objectSpread(_objectSpread({}, minimap), {}, {
    width: orientation === 'landscape' ? minimap.initialSize : minimap.initialSize / ratio,
    height: orientation === 'portrait' ? minimap.initialSize : minimap.initialSize / ratio,
    guideWidth: orientation === 'landscape' ? minimap.initialSize : minimap.initialSize / ratio,
    guideHeight: orientation === 'portrait' ? minimap.initialSize : minimap.initialSize / ratio
  }));
  setOptions(_objectSpread(_objectSpread({}, options), {}, {
    hideZoomControls: options.hideZoomControls || !resizable,
    hideMinimap: options.hideMinimap || !resizable,
    resizable: resizable,
    draggable: false
  }));
};

/** Updates the image, minimap and options based on the scale of the new zoom.
 * Will not allow scaling beyond zoomMax */
var zoom = function zoom(scale, zoomMax, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption) {
  var width = calculateImageWidth({
    container: container,
    orientation: image.orientation,
    ratio: image.ratio,
    scale: scale,
    displayOption: displayOption
  });
  var height = calculateImageHeight({
    container: container,
    orientation: image.orientation,
    ratio: image.ratio,
    scale: scale,
    displayOption: displayOption
  });

  // Reset image position, (i.e. zoom to fit)
  if (scale === 1) {
    var objectFitOffset = calculateObjectFitOffset({
      displayOption: displayOption,
      container: container,
      image: image
    });
    setImage(_objectSpread(_objectSpread({}, image), {}, {
      width: width,
      height: height,
      scale: 1,
      offsetX: 0,
      offsetY: displayOption ? 0 : container.height / 2 - height / 2,
      objectFitOffsetY: objectFitOffset.y,
      objectFitOffsetX: objectFitOffset.x
    }));
    setMinimap(_objectSpread(_objectSpread({}, minimap), {}, {
      guideHeight: minimap.height,
      guideWidth: minimap.width,
      offsetX: 0,
      offsetY: 0
    }));
    //
    setOptions(_objectSpread(_objectSpread({}, options), {}, {
      draggable: false
    }));
  }
  // Actual zooming in request
  else if (scale > 1) {
    if (zoomMax && scale < zoomMax || image.initialWidth > width || image.initialHeight > height) {
      var guideWidth = container.width >= width ? minimap.width : minimap.width / (width / container.width);
      var guideHeight = container.height >= height ? minimap.height : minimap.height / (height / container.height);
      var deltaX = Math.round(width - image.width);
      var deltaY = Math.round(height - image.height);
      var guideDeltaX = Math.round(guideWidth - minimap.guideWidth);
      var guideDeltaY = Math.round(guideHeight - minimap.guideHeight);
      var offsetX = image.offsetX - deltaX / 2;
      var offsetY = image.offsetY - deltaY / 2;
      var guideOffsetX = Math.round(minimap.offsetX - guideDeltaX / 2);
      var guideOffsetY = Math.round(minimap.offsetY - guideDeltaY / 2);
      var offsetXMax = -Math.abs(Math.round(container.width - width));
      var offsetYMax = -Math.abs(Math.round(container.height - height));
      var guideOffsetXMax = Math.round(minimap.width - guideWidth);
      var guideOffsetYMax = Math.round(minimap.height - guideHeight);

      // Reset draggability if we're zooming
      setOptions(_objectSpread(_objectSpread({}, options), {}, {
        draggable: true
      }));
      setImage(_objectSpread(_objectSpread({}, image), {}, {
        width: width,
        height: height,
        scale: scale,
        offsetX: offsetX >= 0 || container.width > width ? 0 : image.offsetX <= offsetXMax ? offsetXMax : offsetX,
        offsetY: container.height > height ? container.height / 2 - height / 2 : offsetY >= 0 ? 0 : image.offsetY < offsetYMax ? offsetYMax : offsetY,
        objectFitOffsetY: 0,
        objectFitOffsetX: 0
      }));
      setMinimap(_objectSpread(_objectSpread({}, minimap), {}, {
        guideWidth: guideWidth,
        guideHeight: guideHeight,
        offsetX: guideOffsetX <= 0 ? 0 : minimap.offsetX < guideOffsetXMax ? guideOffsetX : guideOffsetXMax,
        offsetY: guideOffsetY <= 0 || height < container.height ? 0 : minimap.offsetY < guideOffsetYMax ? guideOffsetY : guideOffsetYMax
      }));
    }
  }
};

/**
 * Since the imageElement is not at the borders of the screen, we need to calculate the X and Y offsets of the Image from the parents
 * @param {*} imageElement
 * @return {object} top, left pixels that indicate where the image is placed on the page
 */
var getAccumulatedOffset = function getAccumulatedOffset(imageElement) {
  // TODO: replace with simpler approach and share as utility function: https://stackoverflow.com/questions/5601659/how-do-you-calculate-the-page-position-of-a-dom-element-when-the-body-can-be-rel
  var offset = {
    top: imageElement.offsetTop,
    left: imageElement.offsetLeft
  };
  var ancestor = imageElement.offsetParent;
  while (ancestor) {
    offset.top += ancestor.offsetTop;
    offset.left += ancestor.offsetLeft;
    ancestor = ancestor.offsetParent;
  }
  return offset;
};

/**
 * The image element might use the css objectFit which can add some additional offset between the element
 * edges and the actual visual image. This function can tell us if the cursor is on this offset.
 * @param {*} mouseEvent
 * @param {object} image with { height, width, objectFitOffsetY, objectFitOffsetX }
 * @returns
 */
var cursorIsOnObjectFitOffset = function cursorIsOnObjectFitOffset(mouseEvent, _ref7, displayOption) {
  var height = _ref7.height,
    width = _ref7.width,
    objectFitOffsetY = _ref7.objectFitOffsetY,
    objectFitOffsetX = _ref7.objectFitOffsetX;
  if (displayOption === 'contain') {
    var accumelatedOffset = getAccumulatedOffset(mouseEvent.currentTarget);
    var posY = mouseEvent.clientY - accumelatedOffset.top;
    var posX = mouseEvent.clientX - accumelatedOffset.left;
    return posY < objectFitOffsetY || posX < objectFitOffsetX || posY > objectFitOffsetY + height || posX > width + objectFitOffsetX;
  }
  return false;
};

/** Calculates the mouse click position in percentage and returns the
 * result in a callback if a hotspot should be added */
var handleMouseUp = function handleMouseUp(_ref8) {
  var event = _ref8.event,
    image = _ref8.image,
    cursor = _ref8.cursor,
    setCursor = _ref8.setCursor,
    isEditable = _ref8.isEditable,
    callback = _ref8.callback;
  setCursor(function (newCursor) {
    return _objectSpread(_objectSpread({}, newCursor), {}, {
      dragPrepared: false,
      imageMousedown: false
    });
  });
  // We only trigger the callback if the image is editable and the intitiating
  // mouse down event was on the actual image (as oppose of outside).
  if (isEditable && cursor !== null && cursor !== void 0 && cursor.imageMousedown) {
    var accumelatedOffset = getAccumulatedOffset(event.currentTarget);
    var relativePosition = {
      x: event.clientX - (accumelatedOffset.left + image.objectFitOffsetX),
      y: event.clientY - (accumelatedOffset.top + image.objectFitOffsetY)
    };
    var percentagePosition = {
      x: relativePosition.x / image.width * 100,
      y: relativePosition.y / image.height * 100
    };
    if (callback) {
      callback(percentagePosition);
    }
  }
};

/** Parent smart component with local state that renders an image with its hotspots */
var ImageHotspots = function ImageHotspots(_ref9) {
  var hideZoomControlsProp = _ref9.hideZoomControls,
    hideHotspotsProp = _ref9.hideHotspots,
    hideMinimapProp = _ref9.hideMinimap,
    minimapBehavior = _ref9.minimapBehavior,
    hotspots = _ref9.hotspots,
    i18n = _ref9.i18n,
    background = _ref9.background,
    src = _ref9.src,
    id = _ref9.id,
    height = _ref9.height,
    width = _ref9.width,
    alt = _ref9.alt,
    icons = _ref9.icons,
    isEditable = _ref9.isEditable,
    isHotspotDataLoading = _ref9.isHotspotDataLoading,
    onAddHotspotPosition = _ref9.onAddHotspotPosition,
    onUpdateHotspotPosition = _ref9.onUpdateHotspotPosition,
    onSelectHotspot = _ref9.onSelectHotspot,
    onHotspotContentChanged = _ref9.onHotspotContentChanged,
    zoomMax = _ref9.zoomMax,
    renderIconByName = _ref9.renderIconByName,
    locale = _ref9.locale,
    selectedHotspots = _ref9.selectedHotspots,
    displayOption = _ref9.displayOption;
  // Need to keep track of whether the Ctrl key is currently pressed because we want to only add hotspots in that case
  var _useState = useState(),
    _useState2 = _slicedToArray(_useState, 2),
    isCtrlPressed = _useState2[0],
    setIsCtrlPressed = _useState2[1];
  // Tracks if the crossHair cursor should be allowed based on mouse position
  var _useState3 = useState(false),
    _useState4 = _slicedToArray(_useState3, 2),
    allowCrossHair = _useState4[0],
    setAllowCrossHair = _useState4[1];
  // Image needs to be stored in state because we're dragging it around when zoomed in, and we need to keep track of when it loads
  var _useState5 = useState({}),
    _useState6 = _slicedToArray(_useState5, 2),
    image = _useState6[0],
    setImage = _useState6[1];
  // Minimap needs to be stored in state because we're dragging it around when zoomed in
  var _useState7 = useState({
      initialSize: 100,
      offsetX: 0,
      offsetY: 0
    }),
    _useState8 = _slicedToArray(_useState7, 2),
    minimap = _useState8[0],
    setMinimap = _useState8[1];
  var _useState9 = useState({}),
    _useState10 = _slicedToArray(_useState9, 2),
    cursor = _useState10[0],
    setCursor = _useState10[1];
  // Options need to be stored in state because based on the zoom level they may change
  var _useState11 = useState({
      hideZoomControls: hideZoomControlsProp,
      hideHotspots: hideHotspotsProp,
      hideMinimap: minimapBehavior !== 'show'
    }),
    _useState12 = _slicedToArray(_useState11, 2),
    options = _useState12[0],
    setOptions = _useState12[1];
  // Tracks if a hotspot is being dragged and which one.
  var _useState13 = useState(null),
    _useState14 = _slicedToArray(_useState13, 2),
    draggingHotspotId = _useState14[0],
    setDraggingHotspotId = _useState14[1];

  // Ref for outer container
  var containerRef = useRef(null);
  var dragStateRef = useRef({
    // Flag to indicate if a drag is active
    isDragging: false,
    // Stores the starting mouse position of a drag
    dragStartPosition: {
      x: 0,
      y: 0
    },
    // Tracks the current position during a drag
    currentDargPosition: null,
    // Stores layout of image and container
    layout: {
      rect: null,
      hotspotLayout: null
    },
    // Tracks Scheduled Animation Frames
    frame: null
  });

  // Minimum pixel movement before a drag is initiated
  var DRAG_THRESHOLD = 5;
  var mergedI18n = useMemo(function () {
    return _objectSpread(_objectSpread({}, defaultProps.i18n), i18n);
  }, [i18n]);
  var hotspotsWithId = useMemo(function () {
    return hotspots.map(function (hotspot, index) {
      return _objectSpread(_objectSpread({}, hotspot), {}, {
        id: "".concat(hotspot.x, "-").concat(hotspot.y, "-").concat(index)
      });
    });
  }, [hotspots]);
  var _useState15 = useState(hotspotsWithId),
    _useState16 = _slicedToArray(_useState15, 2),
    editableHotspots = _useState16[0],
    setEditableHotspots = _useState16[1];
  useEffect(function () {
    setEditableHotspots(hotspotsWithId);
  }, [hotspotsWithId]);
  var handleCtrlKeyUp = useCallback(function (event) {
    // Was the control key unpressed
    if (event.key === keyboardKeys.CONTROL) {
      setIsCtrlPressed(false);
    }
  }, []);
  var handleCtrlKeyDown = useCallback(function (event) {
    if (event.ctrlKey) {
      setIsCtrlPressed(true);
    }
  }, []);

  // Listen to the control key to add hotspots
  useEffect(function () {
    window.addEventListener('keydown', handleCtrlKeyDown);
    window.addEventListener('keyup', handleCtrlKeyUp);
    return function () {
      window.removeEventListener('keydown', handleCtrlKeyDown);
      window.removeEventListener('keyup', handleCtrlKeyUp);
    };
  }, [handleCtrlKeyDown, handleCtrlKeyUp]);
  useEffect(function () {
    setOptions({
      hideZoomControls: hideZoomControlsProp,
      hideHotspots: hideHotspotsProp,
      hideMinimap: minimapBehavior !== 'show'
    });
  }, [hideZoomControlsProp, hideHotspotsProp, minimapBehavior]);
  var orientation = width > height ? 'landscape' : 'portrait';
  var ratio = orientation === 'landscape' ? width / height : height / width;
  var container = {
    height: height,
    width: width,
    ratio: ratio,
    orientation: orientation
  };

  // Once the component mounts set up the container info
  useEffect(function () {
    zoom(image.scale, zoomMax, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption);
  },
  // to prevent needing useDeepCompareEffect, since it's an anti-pattern
  // we split out all the various parts needed by the zoom function and check them
  // individually.
  // eslint-disable-next-line react-hooks/exhaustive-deps
  [container.height, container.width, container.ratio, container.orientation, zoomMax, image.initialHeight, image.initialWidth, image.scale, image.orientation, image.ratio, image.offsetY, image.offsetX, image.objectFitOffsetY, image.objectFitOffsetX, image.width, image.height, minimap.initialSize, minimap.height, minimap.width, minimap.guideHeight, minimap.guideWidth, minimap.offsetX, minimap.offsetY, options.hideZoomControls, options.hideHotspots, options.hideMinimap, options.resizable, options.draggable]);
  var dragging = cursor.dragging,
    dragPrepared = cursor.dragPrepared;
  var hideZoomControls = options.hideZoomControls,
    hideHotspots = options.hideHotspots,
    hideMinimap = options.hideMinimap,
    draggable = options.draggable;
  var imageLoaded = image.initialWidth && image.initialHeight;
  var containerStyle = {
    width: '100%',
    height: '100%',
    position: 'relative',
    overflow: 'hidden',
    textAlign: 'center',
    background: background
  };
  var imageStyle = {
    cursor: allowCrossHair && isCtrlPressed ? 'crosshair' : 'auto',
    position: 'relative',
    left: image.offsetX,
    top: image.offsetY,
    height: displayOption === 'fill' ? image.height : image.scale === 1 ? '100%' : 'auto',
    width: displayOption === 'fill' ? image.width : image.scale === 1 ? '100%' : 'auto',
    objectFit: displayOption
  };
  var onHotspotClicked = useCallback(function (evt, position) {
    // prevent click behavior after drag
    if (dragStateRef.current.isDragging) {
      return;
    }

    // It is possible to receive two events here, one Mouse event and one Pointer event.
    // When used in the ImageHotspots component the Pointer event can somehow be from a
    // previously clicked hotspot. See https://github.com/carbon-design-system/carbon-addons-iot-react/issues/1803
    var isPointerEventOfTypeMouse = (evt === null || evt === void 0 ? void 0 : evt.pointerType) === 'mouse';
    if (!isPointerEventOfTypeMouse && isEditable) {
      onSelectHotspot(position);
    }
  }, [onSelectHotspot, isEditable]);
  var handleMouseDownHotspot = useCallback(function (e, id1) {
    var hotspot = editableHotspots.find(function (h) {
      return h.id === id1;
    });
    if (!isEditable || (hotspot === null || hotspot === void 0 ? void 0 : hotspot.type) === 'dynamic') {
      return;
    }
    e.stopPropagation();
    setDraggingHotspotId(id1);
    dragStateRef.current.isDragging = true;
    dragStateRef.current.dragStartPosition = {
      x: e.clientX,
      y: e.clientY
    };

    // Calculate layout once at drag start
    if (containerRef.current) {
      var rect = containerRef.current.getBoundingClientRect();
      var hotspotLayout = calculateHotspotContainerLayout(image, container, displayOption);
      dragStateRef.current.layout = {
        rect: rect,
        hotspotLayout: hotspotLayout
      };
    }
  }, [container, displayOption, image, isEditable, editableHotspots]);
  var handleMouseMoveHotspot = useCallback(function (e) {
    if (draggingHotspotId !== null && containerRef.current) {
      var _dragStateRef$current = dragStateRef.current,
        isDragging = _dragStateRef$current.isDragging,
        dragStartPosition = _dragStateRef$current.dragStartPosition,
        layout = _dragStateRef$current.layout;
      var dx = e.clientX - dragStartPosition.x;
      var dy = e.clientY - dragStartPosition.y;
      var distance = Math.sqrt(dx * dx + dy * dy);
      if (distance > DRAG_THRESHOLD && isDragging) {
        var rect = layout.rect,
          hotspotLayout = layout.hotspotLayout;
        var x = (e.clientX - rect.left - hotspotLayout.left) / hotspotLayout.width * 100;
        var y = (e.clientY - rect.top - hotspotLayout.top) / hotspotLayout.height * 100;
        // Clamp within image boundaries
        x = Math.max(0, Math.min(100, x));
        y = Math.max(0, Math.min(100, y));
        dragStateRef.current.currentDargPosition = {
          x: x,
          y: y
        };

        // throttle with requestAnimationFrame
        if (dragStateRef.current.frame === null) {
          dragStateRef.current.frame = requestAnimationFrame(function () {
            setEditableHotspots(function (prev) {
              return prev.map(function (item) {
                return item.id === draggingHotspotId ? _objectSpread(_objectSpread({}, item), {}, {
                  x: dragStateRef.current.currentDargPosition.x,
                  y: dragStateRef.current.currentDargPosition.y
                }) : item;
              });
            });
            dragStateRef.current.frame = null;
          });
        }
      }
    }
  }, [draggingHotspotId]);
  var handleMouseUpHotspot = useCallback(function (e) {
    if (draggingHotspotId !== null && dragStateRef.current.isDragging) {
      e.stopPropagation();
      var _ref10 = dragStateRef.current.currentDargPosition || {},
        x = _ref10.x,
        y = _ref10.y;
      onUpdateHotspotPosition({
        newHotspots: editableHotspots,
        position: {
          x: x,
          y: y
        }
      });
      setDraggingHotspotId(null);
      dragStateRef.current.currentDargPosition = null;
      dragStateRef.current.isDragging = false;
    }
  }, [editableHotspots, draggingHotspotId, onUpdateHotspotPosition]);
  useEffect(function () {
    var dragState = dragStateRef.current;
    return function () {
      if (dragState.frame !== null) {
        cancelAnimationFrame(dragState.frame);
      }
    };
  }, []);

  // Listens to mouse movement for dragging hotspot
  useEffect(function () {
    if (!isEditable) return undefined;
    var containerElement = containerRef.current;
    containerElement.addEventListener('mousemove', handleMouseMoveHotspot);
    containerElement.addEventListener('mouseup', handleMouseUpHotspot);
    return function () {
      containerElement.removeEventListener('mousemove', handleMouseMoveHotspot);
      containerElement.removeEventListener('mouseup', handleMouseUpHotspot);
    };
  }, [draggingHotspotId, handleMouseMoveHotspot, handleMouseUpHotspot, isEditable]);
  var getIconRenderFunction = useCallback(function () {
    return renderIconByName || (Array.isArray(icons) ? function (name, props) {
      var _icons$find;
      var Icon = (_icons$find = icons.find(function (iconObj) {
        return iconObj.id === name;
      })) === null || _icons$find === void 0 ? void 0 : _icons$find.icon;
      if (!Icon) {
        {
          warning(false, "An array of available icons was provided to the ImageHotspots but a hotspot was trying to use an icon with name '".concat(name, "' that was not found in that array.")) ;
        }
        return null;
      }
      return /*#__PURE__*/React__default.createElement(Icon, props);
    } : null);
  }, [icons, renderIconByName]);

  // Performance improvement
  var cachedHotspots = useMemo(function () {
    return editableHotspots.map(function (hotspot, index) {
      var _hotspot$content;
      var x = hotspot.x,
        y = hotspot.y;
      var hotspotIsSelected = !!selectedHotspots.find(function (pos) {
        return x === pos.x && y === pos.y;
      });
      // Determine whether the icon needs to be dynamically overridden by a threshold
      var matchingAttributeThresholds = [];
      if ((_hotspot$content = hotspot.content) !== null && _hotspot$content !== void 0 && _hotspot$content.attributes) {
        hotspot.content.attributes.forEach(function (_ref11) {
          var _hotspot$content2;
          var thresholds = _ref11.thresholds,
            dataSourceId = _ref11.dataSourceId;
          if (!isEmpty(thresholds) && !isEmpty((_hotspot$content2 = hotspot.content) === null || _hotspot$content2 === void 0 ? void 0 : _hotspot$content2.values)) {
            var _hotspot$content3;
            var attributeThresholds = findMatchingThresholds(thresholds.map(function (threshold) {
              return _objectSpread(_objectSpread({}, threshold), {}, {
                dataSourceId: dataSourceId
              });
            }), (_hotspot$content3 = hotspot.content) === null || _hotspot$content3 === void 0 ? void 0 : _hotspot$content3.values, dataSourceId);
            matchingAttributeThresholds.push.apply(matchingAttributeThresholds, _toConsumableArray(attributeThresholds));
          }
        });
      }
      return /*#__PURE__*/React__default.createElement(Hotspot, _extends({}, _omit(hotspot, 'content'), {
        content: /*#__PURE__*/React__default.isValidElement(hotspot.content) ? hotspot.content : /*#__PURE__*/React__default.createElement(HotspotContent, _extends({}, hotspot.content, {
          locale: locale,
          renderIconByName: getIconRenderFunction(),
          id: "hotspot-content-".concat(x, "-").concat(y),
          isTitleEditable: (hotspotIsSelected && hotspot.type === 'text'),
          onChange: onHotspotContentChanged,
          i18n: mergedI18n
        })),
        icon: !isEmpty(matchingAttributeThresholds) ? matchingAttributeThresholds[0].icon : hotspot.icon,
        color: !isEmpty(matchingAttributeThresholds) ? matchingAttributeThresholds[0].color : hotspot.color,
        key: "".concat(x, "-").concat(y, "-").concat(index),
        renderIconByName: getIconRenderFunction(),
        isSelected: hotspotIsSelected,
        onClick: onHotspotClicked,
        onMouseDown: function onMouseDown(e) {
          return handleMouseDownHotspot(e, hotspot.id);
        }
      }));
    });
  }, [editableHotspots, selectedHotspots, locale, getIconRenderFunction, isEditable, onHotspotContentChanged, mergedI18n, onHotspotClicked, handleMouseDownHotspot]);
  var hotspotsStyle = {
    position: 'absolute',
    left: image.offsetX,
    right: image.offsetX >= 0 ? 0 : 'auto',
    margin: 'auto',
    pointerEvents: 'none'
  };
  if (imageLoaded) {
    if (container.orientation === 'landscape') {
      imageStyle.height = displayOption && image.scale === 1 ? '100%' : image.height;
      if (!displayOption && image.orientation === 'portrait') {
        imageStyle.width = image.height / image.ratio;
      } else if (!displayOption && image.orientation === 'landscape' && image.width < container.width) {
        imageStyle.width = image.width;
      }
    } else {
      imageStyle.width = displayOption && image.scale === 1 ? '100%' : image.width;
      imageStyle.height = displayOption ? imageStyle.height : image.height;
    }
    var hotspotLayout = calculateHotspotContainerLayout(image, container, displayOption);
    hotspotsStyle.height = hotspotLayout.height;
    hotspotsStyle.width = hotspotLayout.width;
    hotspotsStyle.top = hotspotLayout.top;
  }
  /* eslint-disable jsx-a11y/no-noninteractive-element-interactions */
  return /*#__PURE__*/React__default.createElement("div", {
    onContextMenu: isEditable ? function (event) {
      // if we're in edit mode, prevent the CTRL-click context menu from popping
      event.preventDefault();
    } : undefined,
    style: containerStyle,
    onMouseOut: function onMouseOut() {
      if (dragging) {
        // If we leave the container, stop detecting the drag
        stopDrag(cursor, setCursor);
      }
    },
    onBlur: function onBlur() {
      if (dragging) {
        // If we leave the container, stop detecting the drag
        stopDrag(cursor, setCursor);
      }
    },
    ref: containerRef
  }, src ? /*#__PURE__*/React__default.createElement("img", {
    id: id,
    className: "".concat(iotPrefix, "--image-card-img"),
    src: src,
    alt: alt,
    onLoad: function onLoad(event) {
      return onImageLoad(event, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption);
    },
    style: imageStyle,
    onMouseDown: function onMouseDown(evt) {
      if (!hideZoomControls && draggable) {
        prepareDrag(evt, cursor, setCursor);
      } else {
        setCursor(_objectSpread(_objectSpread({}, cursor), {}, {
          imageMousedown: !cursorIsOnObjectFitOffset(evt, image, displayOption)
        }));
      }
    },
    onMouseMove: function onMouseMove(evt) {
      if (!dragging) {
        setAllowCrossHair(isEditable && !cursorIsOnObjectFitOffset(evt, image, displayOption));
      }
      if (!hideZoomControls && draggable && dragPrepared) {
        startDrag(evt, 'image', cursor, setCursor);
      } else if (!hideZoomControls && dragging) {
        whileDrag(evt, cursor, setCursor, image, setImage, minimap, setMinimap);
      }
    },
    onMouseUp: function onMouseUp(event) {
      if (dragging) {
        stopDrag(cursor, setCursor);
      } else {
        handleMouseUp({
          event: event,
          image: image,
          cursor: cursor,
          setCursor: setCursor,
          isEditable: isEditable,
          callback: event.ctrlKey ? onAddHotspotPosition : undefined
        });
      }
    }
  }) : null, isHotspotDataLoading ? /*#__PURE__*/React__default.createElement(InlineLoading, {
    style: {
      position: 'absolute',
      top: 0
    },
    description: "Loading hotspot data...",
    status: "active"
  }) : null, !isHotspotDataLoading && !hideHotspots && hotspots ? /*#__PURE__*/React__default.createElement("div", {
    "data-testid": "".concat(id, "-hotspots-container"),
    style: hotspotsStyle
  }, cachedHotspots) : null, !hideZoomControls ? /*#__PURE__*/React__default.createElement(ImageControls, {
    i18n: mergedI18n,
    minimap: _objectSpread(_objectSpread({}, minimap), {}, {
      src: src
    }),
    draggable: draggable,
    dragging: dragging,
    hideMinimap: minimapBehavior === 'showOnPan' ? !dragging || hideMinimapProp : hideMinimap,
    onZoomToFit: function onZoomToFit() {
      return zoom(1, zoomMax, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption);
    },
    onZoomIn: function onZoomIn() {
      return zoom(image.scale + 1, zoomMax, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption);
    },
    onZoomOut: function onZoomOut() {
      return zoom(image.scale - 1, zoomMax, container, image, setImage, minimap, setMinimap, options, setOptions, displayOption);
    }
  }) : null);
};
ImageHotspots.propTypes = propTypes;
ImageHotspots.defaultProps = defaultProps;
ImageHotspots.__docgenInfo = {
  "description": "Parent smart component with local state that renders an image with its hotspots",
  "methods": [],
  "displayName": "ImageHotspots",
  "props": {
    "id": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "id of the local image file to display",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "src": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "source of the local image file to display",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "displayOption": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "value for object-fit property - 'contain' or 'fill'",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "hotspots": {
      "defaultValue": {
        "value": "[]",
        "computed": false
      },
      "description": "optional array of hotspots to render over the image",
      "type": {
        "name": "arrayOf",
        "value": {
          "name": "shape",
          "value": "import { HotspotIconPropType, HotspotPropTypes } from '../../constants/SharedPropTypes';",
          "computed": true
        }
      },
      "required": false
    },
    "alt": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "alt tag and shown on mouseover",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "hideZoomControls": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "optional features to enable or disable",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "hideHotspots": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "hideMinimap": {
      "defaultValue": {
        "value": "undefined",
        "computed": true
      },
      "description": "",
      "type": {
        "name": "custom",
        "raw": "(props, propName, componentName) => {\n  if (__DEV__) {\n    const value = props[propName];\n    /* istanbul ignore else */\n    if (typeof value !== 'undefined') {\n      return new Error(\n        `${componentName}: '${propName}' prop is deprecated in favor of 'minimapBehavior=\"${\n          value ? 'hide' : 'show'\n        }\"'.`\n      );\n    }\n  }\n}"
      },
      "required": false
    },
    "minimapBehavior": {
      "defaultValue": {
        "value": "'showOnPan'",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "enum",
        "value": [{
          "value": "'hide'",
          "computed": false
        }, {
          "value": "'show'",
          "computed": false
        }, {
          "value": "'showOnPan'",
          "computed": false
        }]
      },
      "required": false
    },
    "isHotspotDataLoading": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "isEditable": {
      "defaultValue": {
        "value": "false",
        "computed": false
      },
      "description": "when true activates mouse event based create & select hotspot fuctionality",
      "type": {
        "name": "bool"
      },
      "required": false
    },
    "onAddHotspotPosition": {
      "defaultValue": {
        "value": "() => {}",
        "computed": false
      },
      "description": "Callback when an editable image is clicked without drag, used to add hotspot.\nEmits position obj {x, y} of hotspot to be added.",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onUpdateHotspotPosition": {
      "defaultValue": {
        "value": "() => {}",
        "computed": false
      },
      "description": "Callback when a hotspot is dragged to new position in isEditable mode\nEmits new hotspots and updated position obj {x, y} of hotspot.",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onSelectHotspot": {
      "defaultValue": {
        "value": "() => {}",
        "computed": false
      },
      "description": "Callback when a hotspot is clicked in isEditable mode, emits position obj {x, y}",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "onHotspotContentChanged": {
      "defaultValue": {
        "value": "() => {}",
        "computed": false
      },
      "description": "Callback when the hotspot content is changed, emits position obj {x, y}\nand content change obj, e.g. {title: 'new title value'}",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "background": {
      "defaultValue": {
        "value": "'#eee'",
        "computed": false
      },
      "description": "Background color to display around the image",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "zoomMax": {
      "defaultValue": {
        "value": "undefined",
        "computed": true
      },
      "description": "",
      "type": {
        "name": "number"
      },
      "required": false
    },
    "renderIconByName": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "func"
      },
      "required": false
    },
    "i18n": {
      "defaultValue": {
        "value": "{\n  zoomIn: 'Zoom in',\n  zoomOut: 'Zoom out',\n  zoomToFit: 'Zoom to fit',\n  titlePlaceholderText: 'Enter label',\n  titleEditableHintText: 'Click to edit label',\n}",
        "computed": false
      },
      "description": "",
      "type": {
        "name": "shape",
        "value": {
          "zoomIn": {
            "name": "string",
            "required": false
          },
          "zoomOut": {
            "name": "string",
            "required": false
          },
          "zoomToFit": {
            "name": "string",
            "required": false
          },
          "titlePlaceholderText": {
            "name": "string",
            "required": false
          },
          "titleEditableHintText": {
            "name": "string",
            "required": false
          }
        }
      },
      "required": false
    },
    "locale": {
      "defaultValue": {
        "value": "undefined",
        "computed": true
      },
      "description": "locale string to pass for formatting",
      "type": {
        "name": "string"
      },
      "required": false
    },
    "selectedHotspots": {
      "defaultValue": {
        "value": "[]",
        "computed": false
      },
      "description": "The (unique) positions of the currently selected hotspots",
      "type": {
        "name": "arrayOf",
        "value": {
          "name": "shape",
          "value": {
            "x": {
              "name": "number",
              "required": false
            },
            "y": {
              "name": "number",
              "required": false
            }
          }
        }
      },
      "required": false
    },
    "icons": {
      "defaultValue": {
        "value": "null",
        "computed": false
      },
      "description": "Array of identifiable icons used by the hotspots. The icons here will be used to\nrender the hotspot icons unless the renderIconByName prop us used.",
      "type": {
        "name": "arrayOf",
        "value": {
          "name": "custom",
          "raw": "HotspotIconPropType"
        }
      },
      "required": false
    },
    "height": {
      "description": "Current height in pixels",
      "type": {
        "name": "number"
      },
      "required": true
    },
    "width": {
      "description": "Current width in pixels",
      "type": {
        "name": "number"
      },
      "required": true
    }
  }
};
var ImageHotspots$1 = ImageHotspots;

export { calculateHotspotContainerLayout, calculateImageHeight, calculateImageWidth, calculateObjectFitOffset, cursorIsOnObjectFitOffset, ImageHotspots$1 as default, getAccumulatedOffset, handleMouseUp, onImageLoad, prepareDrag, startDrag, stopDrag, whileDrag, zoom };
