import { __assign } from 'tslib';
import { DirectionalHint } from '../../common/DirectionalHint.js';
import { Rectangle, getScrollbarWidth, getRTL } from '@uifabric-vue/utilities';
import { RectangleEdge } from './positioning.types.js';

var _a;
function _createPositionData(targetEdge, alignmentEdge, isAuto) {
  return {
    targetEdge: targetEdge,
    alignmentEdge: alignmentEdge,
    isAuto: isAuto
  };
}
var DirectionalDictionary = (_a = {}, _a[DirectionalHint.topLeftEdge] = _createPositionData(RectangleEdge.top, RectangleEdge.left), _a[DirectionalHint.topCenter] = _createPositionData(RectangleEdge.top), _a[DirectionalHint.topRightEdge] = _createPositionData(RectangleEdge.top, RectangleEdge.right), _a[DirectionalHint.topAutoEdge] = _createPositionData(RectangleEdge.top, void 0, true), _a[DirectionalHint.bottomLeftEdge] = _createPositionData(RectangleEdge.bottom, RectangleEdge.left), _a[DirectionalHint.bottomCenter] = _createPositionData(RectangleEdge.bottom), _a[DirectionalHint.bottomRightEdge] = _createPositionData(RectangleEdge.bottom, RectangleEdge.right), _a[DirectionalHint.bottomAutoEdge] = _createPositionData(RectangleEdge.bottom, void 0, true), _a[DirectionalHint.leftTopEdge] = _createPositionData(RectangleEdge.left, RectangleEdge.top), _a[DirectionalHint.leftCenter] = _createPositionData(RectangleEdge.left), _a[DirectionalHint.leftBottomEdge] = _createPositionData(RectangleEdge.left, RectangleEdge.bottom), _a[DirectionalHint.rightTopEdge] = _createPositionData(RectangleEdge.right, RectangleEdge.top), _a[DirectionalHint.rightCenter] = _createPositionData(RectangleEdge.right), _a[DirectionalHint.rightBottomEdge] = _createPositionData(RectangleEdge.right, RectangleEdge.bottom), _a);
function _isRectangleWithinBounds(rect, boundingRect) {
  if (rect.top < boundingRect.top) {
    return false;
  }
  if (rect.bottom > boundingRect.bottom) {
    return false;
  }
  if (rect.left < boundingRect.left) {
    return false;
  }
  if (rect.right > boundingRect.right) {
    return false;
  }
  return true;
}
function _getOutOfBoundsEdges(rect, boundingRect) {
  var outOfBounds = [];
  if (rect.top < boundingRect.top) {
    outOfBounds.push(RectangleEdge.top);
  }
  if (rect.bottom > boundingRect.bottom) {
    outOfBounds.push(RectangleEdge.bottom);
  }
  if (rect.left < boundingRect.left) {
    outOfBounds.push(RectangleEdge.left);
  }
  if (rect.right > boundingRect.right) {
    outOfBounds.push(RectangleEdge.right);
  }
  return outOfBounds;
}
function _getEdgeValue(rect, edge) {
  return rect[RectangleEdge[edge]];
}
function _setEdgeValue(rect, edge, value) {
  rect[RectangleEdge[edge]] = value;
  return rect;
}
function _getCenterValue(rect, edge) {
  var edges = _getFlankingEdges(edge);
  return (_getEdgeValue(rect, edges.positiveEdge) + _getEdgeValue(rect, edges.negativeEdge)) / 2;
}
function _getRelativeEdgeValue(edge, value) {
  if (edge > 0) {
    return value;
  } else {
    return value * -1;
  }
}
function _getRelativeRectEdgeValue(edge, rect) {
  return _getRelativeEdgeValue(edge, _getEdgeValue(rect, edge));
}
function _getRelativeEdgeDifference(rect, hostRect, edge) {
  var edgeDifference = _getEdgeValue(rect, edge) - _getEdgeValue(hostRect, edge);
  return _getRelativeEdgeValue(edge, edgeDifference);
}
function _moveEdge(rect, edge, newValue) {
  var difference = _getEdgeValue(rect, edge) - newValue;
  rect = _setEdgeValue(rect, edge, newValue);
  rect = _setEdgeValue(rect, edge * -1, _getEdgeValue(rect, edge * -1) - difference);
  return rect;
}
function _alignEdges(rect, target, edge, gap) {
  if (gap === void 0) {
    gap = 0;
  }
  return _moveEdge(rect, edge, _getEdgeValue(target, edge) + _getRelativeEdgeValue(edge, gap));
}
function _alignOppositeEdges(rect, target, targetEdge, gap) {
  if (gap === void 0) {
    gap = 0;
  }
  var oppositeEdge = targetEdge * -1;
  var adjustedGap = _getRelativeEdgeValue(oppositeEdge, gap);
  return _moveEdge(rect, targetEdge * -1, _getEdgeValue(target, targetEdge) + adjustedGap);
}
function _isEdgeInBounds(rect, bounds, edge) {
  var adjustedRectValue = _getRelativeRectEdgeValue(edge, rect);
  return adjustedRectValue > _getRelativeRectEdgeValue(edge, bounds);
}
function _flipToFit(rect, target, bounding, positionData, gap) {
  if (gap === void 0) {
    gap = 0;
  }
  var directions = [
    RectangleEdge.left,
    RectangleEdge.right,
    RectangleEdge.bottom,
    RectangleEdge.top
  ];
  if (getRTL()) {
    directions[0] *= -1;
    directions[1] *= -1;
  }
  var currentEstimate = rect;
  var currentEdge = positionData.targetEdge;
  var currentAlignment = positionData.alignmentEdge;
  for (var i = 0; i < 4; i++) {
    if (!_isEdgeInBounds(currentEstimate, bounding, currentEdge)) {
      directions.splice(directions.indexOf(currentEdge), 1);
      if (directions.length > 0) {
        if (directions.indexOf(currentEdge * -1) > -1) {
          currentEdge = currentEdge * -1;
        } else {
          currentAlignment = currentEdge;
          currentEdge = directions.slice(-1)[0];
        }
        currentEstimate = _estimatePosition(rect, target, { targetEdge: currentEdge, alignmentEdge: currentAlignment }, gap);
      }
    } else {
      return {
        elementRectangle: currentEstimate,
        targetEdge: currentEdge,
        alignmentEdge: currentAlignment
      };
    }
  }
  return {
    elementRectangle: rect,
    targetEdge: positionData.targetEdge,
    alignmentEdge: positionData.alignmentEdge
  };
}
function _flipAlignmentEdge(elementEstimate, target, gap, coverTarget) {
  var alignmentEdge = elementEstimate.alignmentEdge, targetEdge = elementEstimate.targetEdge, elementRectangle = elementEstimate.elementRectangle;
  var oppositeEdge = alignmentEdge * -1;
  var newEstimate = _estimatePosition(elementRectangle, target, { targetEdge: targetEdge, alignmentEdge: oppositeEdge }, gap, coverTarget);
  return {
    elementRectangle: newEstimate,
    targetEdge: targetEdge,
    alignmentEdge: oppositeEdge
  };
}
function _adjustFitWithinBounds(element, target, bounding, positionData, gap, directionalHintFixed, coverTarget) {
  if (gap === void 0) {
    gap = 0;
  }
  var alignmentEdge = positionData.alignmentEdge, alignTargetEdge = positionData.alignTargetEdge;
  var elementEstimate = {
    elementRectangle: element,
    targetEdge: positionData.targetEdge,
    alignmentEdge: alignmentEdge
  };
  if (!directionalHintFixed && !coverTarget) {
    elementEstimate = _flipToFit(element, target, bounding, positionData, gap);
  }
  var outOfBounds = _getOutOfBoundsEdges(element, bounding);
  if (alignTargetEdge) {
    if (elementEstimate.alignmentEdge && outOfBounds.indexOf(elementEstimate.alignmentEdge * -1) > -1) {
      var flippedElementEstimate = _flipAlignmentEdge(elementEstimate, target, gap, coverTarget);
      if (_isRectangleWithinBounds(flippedElementEstimate.elementRectangle, bounding)) {
        return flippedElementEstimate;
      } else {
        elementEstimate = _alignOutOfBoundsEdges(_getOutOfBoundsEdges(flippedElementEstimate.elementRectangle, bounding), elementEstimate, bounding);
      }
    }
  } else {
    elementEstimate = _alignOutOfBoundsEdges(outOfBounds, elementEstimate, bounding);
  }
  return elementEstimate;
}
function _alignOutOfBoundsEdges(outOfBoundsEdges, elementEstimate, bounding) {
  for (var _i = 0, outOfBoundsEdges_1 = outOfBoundsEdges; _i < outOfBoundsEdges_1.length; _i++) {
    var direction = outOfBoundsEdges_1[_i];
    elementEstimate.elementRectangle = _alignEdges(elementEstimate.elementRectangle, bounding, direction);
  }
  return elementEstimate;
}
function _centerEdgeToPoint(rect, edge, point) {
  var positiveEdge = _getFlankingEdges(edge).positiveEdge;
  var elementMiddle = _getCenterValue(rect, edge);
  var distanceToMiddle = elementMiddle - _getEdgeValue(rect, positiveEdge);
  return _moveEdge(rect, positiveEdge, point - distanceToMiddle);
}
function _estimatePosition(elementToPosition, target, positionData, gap, coverTarget) {
  if (gap === void 0) {
    gap = 0;
  }
  var estimatedElementPosition;
  var alignmentEdge = positionData.alignmentEdge, targetEdge = positionData.targetEdge;
  var elementEdge = coverTarget ? targetEdge : targetEdge * -1;
  estimatedElementPosition = coverTarget ? _alignEdges(elementToPosition, target, targetEdge, gap) : _alignOppositeEdges(elementToPosition, target, targetEdge, gap);
  if (!alignmentEdge) {
    var targetMiddlePoint = _getCenterValue(target, targetEdge);
    estimatedElementPosition = _centerEdgeToPoint(estimatedElementPosition, elementEdge, targetMiddlePoint);
  } else {
    estimatedElementPosition = _alignEdges(estimatedElementPosition, target, alignmentEdge);
  }
  return estimatedElementPosition;
}
function _getFlankingEdges(edge) {
  if (edge === RectangleEdge.top || edge === RectangleEdge.bottom) {
    return {
      positiveEdge: RectangleEdge.left,
      negativeEdge: RectangleEdge.right
    };
  } else {
    return {
      positiveEdge: RectangleEdge.top,
      negativeEdge: RectangleEdge.bottom
    };
  }
}
function _finalizeReturnEdge(elementRectangle, returnEdge, bounds) {
  if (bounds && Math.abs(_getRelativeEdgeDifference(elementRectangle, bounds, returnEdge)) > Math.abs(_getRelativeEdgeDifference(elementRectangle, bounds, returnEdge * -1))) {
    return returnEdge * -1;
  }
  return returnEdge;
}
function _finalizeElementPosition(elementRectangle, hostElement, targetEdge, bounds, alignmentEdge, coverTarget, doNotFinalizeReturnEdge) {
  var returnValue = {};
  var hostRect = _getRectangleFromElement(hostElement);
  var elementEdge = coverTarget ? targetEdge : targetEdge * -1;
  var elementEdgeString = RectangleEdge[elementEdge];
  var returnEdge = alignmentEdge || _getFlankingEdges(targetEdge).positiveEdge;
  if (!doNotFinalizeReturnEdge) {
    returnEdge = _finalizeReturnEdge(elementRectangle, returnEdge, bounds);
  }
  returnValue[elementEdgeString] = _getRelativeEdgeDifference(elementRectangle, hostRect, elementEdge);
  returnValue[RectangleEdge[returnEdge]] = _getRelativeEdgeDifference(elementRectangle, hostRect, returnEdge);
  return returnValue;
}
function _calculateActualBeakWidthInPixels(beakWidth) {
  return Math.sqrt(beakWidth * beakWidth * 2);
}
function _getPositionData(directionalHint, directionalHintForRTL, previousPositions) {
  if (directionalHint === void 0) {
    directionalHint = DirectionalHint.bottomAutoEdge;
  }
  if (previousPositions) {
    return {
      alignmentEdge: previousPositions.alignmentEdge,
      isAuto: previousPositions.isAuto,
      targetEdge: previousPositions.targetEdge
    };
  }
  var positionInformation = __assign({}, DirectionalDictionary[directionalHint]);
  if (getRTL()) {
    if (positionInformation.alignmentEdge && positionInformation.alignmentEdge % 2 === 0) {
      positionInformation.alignmentEdge = positionInformation.alignmentEdge * -1;
    }
    return directionalHintForRTL !== void 0 ? DirectionalDictionary[directionalHintForRTL] : positionInformation;
  }
  return positionInformation;
}
function _getAlignmentData(positionData, target, boundingRect, coverTarget, alignTargetEdge) {
  if (positionData.isAuto) {
    positionData.alignmentEdge = getClosestEdge(positionData.targetEdge, target, boundingRect);
  }
  positionData.alignTargetEdge = alignTargetEdge;
  return positionData;
}
function getClosestEdge(targetEdge, target, boundingRect) {
  var targetCenter = _getCenterValue(target, targetEdge);
  var boundingCenter = _getCenterValue(boundingRect, targetEdge);
  var _a2 = _getFlankingEdges(targetEdge), positiveEdge = _a2.positiveEdge, negativeEdge = _a2.negativeEdge;
  if (targetCenter <= boundingCenter) {
    return positiveEdge;
  } else {
    return negativeEdge;
  }
}
function _positionElementWithinBounds(elementToPosition, target, bounding, positionData, gap, directionalHintFixed, coverTarget) {
  var estimatedElementPosition = _estimatePosition(elementToPosition, target, positionData, gap, coverTarget);
  if (_isRectangleWithinBounds(estimatedElementPosition, bounding)) {
    return {
      elementRectangle: estimatedElementPosition,
      targetEdge: positionData.targetEdge,
      alignmentEdge: positionData.alignmentEdge
    };
  } else {
    return _adjustFitWithinBounds(elementToPosition, target, bounding, positionData, gap, directionalHintFixed, coverTarget);
  }
}
function _finalizeBeakPosition(elementPosition, positionedBeak, bounds) {
  var targetEdge = elementPosition.targetEdge * -1;
  var actualElement = new Rectangle(0, elementPosition.elementRectangle.width, 0, elementPosition.elementRectangle.height);
  var returnValue = {};
  var returnEdge = _finalizeReturnEdge(elementPosition.elementRectangle, elementPosition.alignmentEdge ? elementPosition.alignmentEdge : _getFlankingEdges(targetEdge).positiveEdge, bounds);
  returnValue[RectangleEdge[targetEdge]] = _getEdgeValue(positionedBeak, targetEdge);
  returnValue[RectangleEdge[returnEdge]] = _getRelativeEdgeDifference(positionedBeak, actualElement, returnEdge);
  return {
    elementPosition: __assign({}, returnValue),
    closestEdge: getClosestEdge(elementPosition.targetEdge, positionedBeak, actualElement),
    targetEdge: targetEdge
  };
}
function _positionBeak(beakWidth, elementPosition) {
  var target = elementPosition.targetRectangle;
  var _a2 = _getFlankingEdges(elementPosition.targetEdge), positiveEdge = _a2.positiveEdge, negativeEdge = _a2.negativeEdge;
  var beakTargetPoint = _getCenterValue(target, elementPosition.targetEdge);
  var elementBounds = new Rectangle(beakWidth / 2, elementPosition.elementRectangle.width - beakWidth / 2, beakWidth / 2, elementPosition.elementRectangle.height - beakWidth / 2);
  var beakPosition = new Rectangle(0, beakWidth, 0, beakWidth);
  beakPosition = _moveEdge(beakPosition, elementPosition.targetEdge * -1, -beakWidth / 2);
  beakPosition = _centerEdgeToPoint(beakPosition, elementPosition.targetEdge * -1, beakTargetPoint - _getRelativeRectEdgeValue(positiveEdge, elementPosition.elementRectangle));
  if (!_isEdgeInBounds(beakPosition, elementBounds, positiveEdge)) {
    beakPosition = _alignEdges(beakPosition, elementBounds, positiveEdge);
  } else if (!_isEdgeInBounds(beakPosition, elementBounds, negativeEdge)) {
    beakPosition = _alignEdges(beakPosition, elementBounds, negativeEdge);
  }
  return beakPosition;
}
function _getRectangleFromElement(element) {
  var clientRect = element.getBoundingClientRect();
  return new Rectangle(clientRect.left, clientRect.right, clientRect.top, clientRect.bottom);
}
function _getRectangleFromIRect(rect) {
  return new Rectangle(rect.left, rect.right, rect.top, rect.bottom);
}
function _getTargetRect(bounds, target) {
  var targetRectangle;
  if (target) {
    if (!!target.preventDefault) {
      var ev = target;
      targetRectangle = new Rectangle(ev.clientX, ev.clientX, ev.clientY, ev.clientY);
    } else if (!!target.getBoundingClientRect) {
      targetRectangle = _getRectangleFromElement(target);
    } else {
      var point = target;
      var left = point.left || point.x;
      var top_1 = point.top || point.y;
      targetRectangle = new Rectangle(left, left, top_1, top_1);
    }
    if (!_isRectangleWithinBounds(targetRectangle, bounds)) {
      var outOfBounds = _getOutOfBoundsEdges(targetRectangle, bounds);
      for (var _i = 0, outOfBounds_1 = outOfBounds; _i < outOfBounds_1.length; _i++) {
        var direction = outOfBounds_1[_i];
        targetRectangle[RectangleEdge[direction]] = bounds[RectangleEdge[direction]];
      }
    }
  } else {
    targetRectangle = new Rectangle(0, 0, 0, 0);
  }
  return targetRectangle;
}
function _getMaxHeightFromTargetRectangle(targetRectangle, targetEdge, gapSpace, bounds, coverTarget) {
  var maxHeight = 0;
  var directionalHint = DirectionalDictionary[targetEdge];
  var target = coverTarget ? directionalHint.targetEdge * -1 : directionalHint.targetEdge;
  if (target === RectangleEdge.top) {
    maxHeight = _getEdgeValue(targetRectangle, directionalHint.targetEdge) - bounds.top - gapSpace;
  } else if (target === RectangleEdge.bottom) {
    maxHeight = bounds.bottom - _getEdgeValue(targetRectangle, directionalHint.targetEdge) - gapSpace;
  } else {
    maxHeight = bounds.bottom - targetRectangle.top - gapSpace;
  }
  return maxHeight > 0 ? maxHeight : bounds.height;
}
function _positionElementRelative(props, elementToPosition, boundingRect, previousPositions) {
  var gap = props.gapSpace ? props.gapSpace : 0;
  var targetRect = _getTargetRect(boundingRect, props.target);
  var positionData = _getAlignmentData(_getPositionData(props.directionalHint, props.directionalHintForRTL, previousPositions), targetRect, boundingRect, props.coverTarget, props.alignTargetEdge);
  var positionedElement = _positionElementWithinBounds(_getRectangleFromElement(elementToPosition), targetRect, boundingRect, positionData, gap, props.directionalHintFixed, props.coverTarget);
  return __assign(__assign({}, positionedElement), { targetRectangle: targetRect });
}
function _finalizePositionData(positionedElement, hostElement, bounds, coverTarget, doNotFinalizeReturnEdge) {
  var finalizedElement = _finalizeElementPosition(positionedElement.elementRectangle, hostElement, positionedElement.targetEdge, bounds, positionedElement.alignmentEdge, coverTarget, doNotFinalizeReturnEdge);
  return {
    elementPosition: finalizedElement,
    targetEdge: positionedElement.targetEdge,
    alignmentEdge: positionedElement.alignmentEdge
  };
}
function _positionElement(props, hostElement, elementToPosition, previousPositions) {
  var boundingRect = props.bounds ? _getRectangleFromIRect(props.bounds) : new Rectangle(0, window.innerWidth - getScrollbarWidth(), 0, window.innerHeight);
  var positionedElement = _positionElementRelative(props, elementToPosition, boundingRect, previousPositions);
  return _finalizePositionData(positionedElement, hostElement, boundingRect, props.coverTarget);
}
function _positionCallout(props, hostElement, callout, previousPositions, doNotFinalizeReturnEdge) {
  var beakWidth = props.isBeakVisible ? props.beakWidth || 0 : 0;
  var gap = _calculateActualBeakWidthInPixels(beakWidth) / 2 + (props.gapSpace ? props.gapSpace : 0);
  var positionProps = props;
  positionProps.gapSpace = gap;
  var boundingRect = props.bounds ? _getRectangleFromIRect(props.bounds) : new Rectangle(0, window.innerWidth - getScrollbarWidth(), 0, window.innerHeight);
  var positionedElement = _positionElementRelative(positionProps, callout, boundingRect, previousPositions);
  var beakPositioned = _positionBeak(beakWidth, positionedElement);
  var finalizedBeakPosition = _finalizeBeakPosition(positionedElement, beakPositioned, boundingRect);
  return __assign(__assign({}, _finalizePositionData(positionedElement, hostElement, boundingRect, props.coverTarget, doNotFinalizeReturnEdge)), { beakPosition: finalizedBeakPosition });
}
function _positionCard(props, hostElement, callout, previousPositions) {
  return _positionCallout(props, hostElement, callout, previousPositions, true);
}
function positionElement(props, hostElement, elementToPosition, previousPositions) {
  return _positionElement(props, hostElement, elementToPosition, previousPositions);
}
function positionCallout(props, hostElement, elementToPosition, previousPositions) {
  return _positionCallout(props, hostElement, elementToPosition, previousPositions);
}
function positionCard(props, hostElement, elementToPosition, previousPositions) {
  return _positionCard(props, hostElement, elementToPosition, previousPositions);
}
function getMaxHeight(target, targetEdge, gapSpace, bounds, coverTarget) {
  if (gapSpace === void 0) {
    gapSpace = 0;
  }
  var mouseTarget = target;
  var elementTarget = target;
  var pointTarget = target;
  var targetRect;
  var boundingRectangle = bounds ? _getRectangleFromIRect(bounds) : new Rectangle(0, window.innerWidth - getScrollbarWidth(), 0, window.innerHeight);
  var left = pointTarget.left || pointTarget.x;
  var top = pointTarget.top || pointTarget.y;
  if (!!mouseTarget.stopPropagation) {
    targetRect = new Rectangle(mouseTarget.clientX, mouseTarget.clientX, mouseTarget.clientY, mouseTarget.clientY);
  } else if (left !== void 0 && top !== void 0) {
    targetRect = new Rectangle(left, left, top, top);
  } else {
    targetRect = _getRectangleFromElement(elementTarget);
  }
  return _getMaxHeightFromTargetRectangle(targetRect, targetEdge, gapSpace, boundingRectangle, coverTarget);
}
function getOppositeEdge(edge) {
  return edge * -1;
}
function _getBoundsFromTargetWindow(target, targetWindow) {
  var segments;
  if (targetWindow.getWindowSegments) {
    segments = targetWindow.getWindowSegments();
  }
  if (segments === void 0 || segments.length <= 1) {
    return {
      top: 0,
      left: 0,
      right: targetWindow.innerWidth,
      bottom: targetWindow.innerHeight,
      width: targetWindow.innerWidth,
      height: targetWindow.innerHeight
    };
  }
  var x = 0;
  var y = 0;
  if (target !== null && !!target.getBoundingClientRect) {
    var clientRect = target.getBoundingClientRect();
    x = (clientRect.left + clientRect.right) / 2;
    y = (clientRect.top + clientRect.bottom) / 2;
  } else if (target !== null) {
    x = target.left || target.x;
    y = target.top || target.y;
  }
  var bounds = { top: 0, left: 0, right: 0, bottom: 0, width: 0, height: 0 };
  for (var _i = 0, segments_1 = segments; _i < segments_1.length; _i++) {
    var segment = segments_1[_i];
    if (x && segment.left <= x && segment.right >= x && y && segment.top <= y && segment.bottom >= y) {
      bounds = {
        top: segment.top,
        left: segment.left,
        right: segment.right,
        bottom: segment.bottom,
        width: segment.width,
        height: segment.height
      };
    }
  }
  return bounds;
}
function getBoundsFromTargetWindow(target, targetWindow) {
  return _getBoundsFromTargetWindow(target, targetWindow);
}

export { getBoundsFromTargetWindow, getMaxHeight, getOppositeEdge, positionCallout, positionCard, positionElement };
