import { addDisposableListener, getShadowRoot, EventType } from "../../../base/browser/dom.mjs";
import { StandardWheelEvent } from "../../../base/browser/mouseEvent.mjs";
import { TimeoutTimer } from "../../../base/common/async.mjs";
import { Disposable } from "../../../base/common/lifecycle.mjs";
import { isMacintosh } from "../../../base/common/platform.mjs";
import { MouseTargetFactory, HitTestContext, MouseTarget } from "./mouseTarget.mjs";
import { EditorMouseEventFactory, createEditorPagePosition, createCoordinatesRelativeToEditor, GlobalEditorMouseMoveMonitor, ClientCoordinates } from "../editorDom.mjs";
import { EditorZoom } from "../../common/config/editorZoom.mjs";
import { Position } from "../../common/core/position.mjs";
import { Selection } from "../../common/core/selection.mjs";
import { ViewEventHandler } from "../../common/viewEventHandler.mjs";
function createMouseMoveEventMerger(mouseTargetFactory) {
  return function(lastEvent, currentEvent) {
    let targetIsWidget = false;
    if (mouseTargetFactory) {
      targetIsWidget = mouseTargetFactory.mouseTargetIsWidget(currentEvent);
    }
    if (!targetIsWidget) {
      currentEvent.preventDefault();
    }
    return currentEvent;
  };
}
class MouseHandler extends ViewEventHandler {
  constructor(context, viewController, viewHelper) {
    super();
    this._context = context;
    this.viewController = viewController;
    this.viewHelper = viewHelper;
    this.mouseTargetFactory = new MouseTargetFactory(this._context, viewHelper);
    this._mouseDownOperation = this._register(new MouseDownOperation(this._context, this.viewController, this.viewHelper, (e, testEventTarget) => this._createMouseTarget(e, testEventTarget), (e) => this._getMouseColumn(e)));
    this.lastMouseLeaveTime = -1;
    this._height = this._context.configuration.options.get(
      131
      /* layoutInfo */
    ).height;
    const mouseEvents = new EditorMouseEventFactory(this.viewHelper.viewDomNode);
    this._register(mouseEvents.onContextMenu(this.viewHelper.viewDomNode, (e) => this._onContextMenu(e, true)));
    this._register(mouseEvents.onMouseMoveThrottled(this.viewHelper.viewDomNode, (e) => this._onMouseMove(e), createMouseMoveEventMerger(this.mouseTargetFactory), MouseHandler.MOUSE_MOVE_MINIMUM_TIME));
    this._register(mouseEvents.onMouseUp(this.viewHelper.viewDomNode, (e) => this._onMouseUp(e)));
    this._register(mouseEvents.onMouseLeave(this.viewHelper.viewDomNode, (e) => this._onMouseLeave(e)));
    this._register(mouseEvents.onMouseDown(this.viewHelper.viewDomNode, (e) => this._onMouseDown(e)));
    const onMouseWheel = (browserEvent) => {
      this.viewController.emitMouseWheel(browserEvent);
      if (!this._context.configuration.options.get(
        68
        /* mouseWheelZoom */
      )) {
        return;
      }
      const e = new StandardWheelEvent(browserEvent);
      const doMouseWheelZoom = isMacintosh ? (browserEvent.metaKey || browserEvent.ctrlKey) && !browserEvent.shiftKey && !browserEvent.altKey : browserEvent.ctrlKey && !browserEvent.metaKey && !browserEvent.shiftKey && !browserEvent.altKey;
      if (doMouseWheelZoom) {
        const zoomLevel = EditorZoom.getZoomLevel();
        const delta = e.deltaY > 0 ? 1 : -1;
        EditorZoom.setZoomLevel(zoomLevel + delta);
        e.preventDefault();
        e.stopPropagation();
      }
    };
    this._register(addDisposableListener(this.viewHelper.viewDomNode, EventType.MOUSE_WHEEL, onMouseWheel, { capture: true, passive: false }));
    this._context.addEventHandler(this);
  }
  dispose() {
    this._context.removeEventHandler(this);
    super.dispose();
  }
  // --- begin event handlers
  onConfigurationChanged(e) {
    if (e.hasChanged(
      131
      /* layoutInfo */
    )) {
      const height = this._context.configuration.options.get(
        131
        /* layoutInfo */
      ).height;
      if (this._height !== height) {
        this._height = height;
        this._mouseDownOperation.onHeightChanged();
      }
    }
    return false;
  }
  onCursorStateChanged(e) {
    this._mouseDownOperation.onCursorStateChanged(e);
    return false;
  }
  onFocusChanged(e) {
    return false;
  }
  onScrollChanged(e) {
    this._mouseDownOperation.onScrollChanged();
    return false;
  }
  // --- end event handlers
  getTargetAtClientPoint(clientX, clientY) {
    const clientPos = new ClientCoordinates(clientX, clientY);
    const pos = clientPos.toPageCoordinates();
    const editorPos = createEditorPagePosition(this.viewHelper.viewDomNode);
    if (pos.y < editorPos.y || pos.y > editorPos.y + editorPos.height || pos.x < editorPos.x || pos.x > editorPos.x + editorPos.width) {
      return null;
    }
    const relativePos = createCoordinatesRelativeToEditor(this.viewHelper.viewDomNode, editorPos, pos);
    return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), editorPos, pos, relativePos, null);
  }
  _createMouseTarget(e, testEventTarget) {
    let target = e.target;
    if (!this.viewHelper.viewDomNode.contains(target)) {
      const shadowRoot = getShadowRoot(this.viewHelper.viewDomNode);
      if (shadowRoot) {
        target = shadowRoot.elementsFromPoint(e.posx, e.posy).find((el) => this.viewHelper.viewDomNode.contains(el));
      }
    }
    return this.mouseTargetFactory.createMouseTarget(this.viewHelper.getLastRenderData(), e.editorPos, e.pos, e.relativePos, testEventTarget ? target : null);
  }
  _getMouseColumn(e) {
    return this.mouseTargetFactory.getMouseColumn(e.relativePos);
  }
  _onContextMenu(e, testEventTarget) {
    this.viewController.emitContextMenu({
      event: e,
      target: this._createMouseTarget(e, testEventTarget)
    });
  }
  _onMouseMove(e) {
    if (this._mouseDownOperation.isActive()) {
      return;
    }
    const actualMouseMoveTime = e.timestamp;
    if (actualMouseMoveTime < this.lastMouseLeaveTime) {
      return;
    }
    this.viewController.emitMouseMove({
      event: e,
      target: this._createMouseTarget(e, true)
    });
  }
  _onMouseLeave(e) {
    this.lastMouseLeaveTime = (/* @__PURE__ */ new Date()).getTime();
    this.viewController.emitMouseLeave({
      event: e,
      target: null
    });
  }
  _onMouseUp(e) {
    this.viewController.emitMouseUp({
      event: e,
      target: this._createMouseTarget(e, true)
    });
  }
  _onMouseDown(e) {
    const t = this._createMouseTarget(e, true);
    const targetIsContent = t.type === 6 || t.type === 7;
    const targetIsGutter = t.type === 2 || t.type === 3 || t.type === 4;
    const targetIsLineNumbers = t.type === 3;
    const selectOnLineNumbers = this._context.configuration.options.get(
      98
      /* selectOnLineNumbers */
    );
    const targetIsViewZone = t.type === 8 || t.type === 5;
    const targetIsWidget = t.type === 9;
    let shouldHandle = e.leftButton || e.middleButton;
    if (isMacintosh && e.leftButton && e.ctrlKey) {
      shouldHandle = false;
    }
    const focus = () => {
      e.preventDefault();
      this.viewHelper.focusTextArea();
    };
    if (shouldHandle && (targetIsContent || targetIsLineNumbers && selectOnLineNumbers)) {
      focus();
      this._mouseDownOperation.start(t.type, e);
    } else if (targetIsGutter) {
      e.preventDefault();
    } else if (targetIsViewZone) {
      const viewZoneData = t.detail;
      if (this.viewHelper.shouldSuppressMouseDownOnViewZone(viewZoneData.viewZoneId)) {
        focus();
        this._mouseDownOperation.start(t.type, e);
        e.preventDefault();
      }
    } else if (targetIsWidget && this.viewHelper.shouldSuppressMouseDownOnWidget(t.detail)) {
      focus();
      e.preventDefault();
    }
    this.viewController.emitMouseDown({
      event: e,
      target: t
    });
  }
}
MouseHandler.MOUSE_MOVE_MINIMUM_TIME = 100;
class MouseDownOperation extends Disposable {
  constructor(context, viewController, viewHelper, createMouseTarget, getMouseColumn) {
    super();
    this._context = context;
    this._viewController = viewController;
    this._viewHelper = viewHelper;
    this._createMouseTarget = createMouseTarget;
    this._getMouseColumn = getMouseColumn;
    this._mouseMoveMonitor = this._register(new GlobalEditorMouseMoveMonitor(this._viewHelper.viewDomNode));
    this._onScrollTimeout = this._register(new TimeoutTimer());
    this._mouseState = new MouseDownState();
    this._currentSelection = new Selection(1, 1, 1, 1);
    this._isActive = false;
    this._lastMouseEvent = null;
  }
  dispose() {
    super.dispose();
  }
  isActive() {
    return this._isActive;
  }
  _onMouseDownThenMove(e) {
    this._lastMouseEvent = e;
    this._mouseState.setModifiers(e);
    const position = this._findMousePosition(e, true);
    if (!position) {
      return;
    }
    if (this._mouseState.isDragAndDrop) {
      this._viewController.emitMouseDrag({
        event: e,
        target: position
      });
    } else {
      this._dispatchMouse(position, true);
    }
  }
  start(targetType, e) {
    this._lastMouseEvent = e;
    this._mouseState.setStartedOnLineNumbers(
      targetType === 3
      /* GUTTER_LINE_NUMBERS */
    );
    this._mouseState.setStartButtons(e);
    this._mouseState.setModifiers(e);
    const position = this._findMousePosition(e, true);
    if (!position || !position.position) {
      return;
    }
    this._mouseState.trySetCount(e.detail, position.position);
    e.detail = this._mouseState.count;
    const options = this._context.configuration.options;
    if (!options.get(
      81
      /* readOnly */
    ) && options.get(
      31
      /* dragAndDrop */
    ) && !options.get(
      18
      /* columnSelection */
    ) && !this._mouseState.altKey && e.detail < 2 && !this._isActive && !this._currentSelection.isEmpty() && position.type === 6 && position.position && this._currentSelection.containsPosition(position.position)) {
      this._mouseState.isDragAndDrop = true;
      this._isActive = true;
      this._mouseMoveMonitor.startMonitoring(e.target, e.buttons, createMouseMoveEventMerger(null), (e2) => this._onMouseDownThenMove(e2), (browserEvent) => {
        const position2 = this._findMousePosition(this._lastMouseEvent, true);
        if (browserEvent && browserEvent instanceof KeyboardEvent) {
          this._viewController.emitMouseDropCanceled();
        } else {
          this._viewController.emitMouseDrop({
            event: this._lastMouseEvent,
            target: position2 ? this._createMouseTarget(this._lastMouseEvent, true) : null
            // Ignoring because position is unknown, e.g., Content View Zone
          });
        }
        this._stop();
      });
      return;
    }
    this._mouseState.isDragAndDrop = false;
    this._dispatchMouse(position, e.shiftKey);
    if (!this._isActive) {
      this._isActive = true;
      this._mouseMoveMonitor.startMonitoring(e.target, e.buttons, createMouseMoveEventMerger(null), (e2) => this._onMouseDownThenMove(e2), () => this._stop());
    }
  }
  _stop() {
    this._isActive = false;
    this._onScrollTimeout.cancel();
  }
  onHeightChanged() {
    this._mouseMoveMonitor.stopMonitoring();
  }
  onScrollChanged() {
    if (!this._isActive) {
      return;
    }
    this._onScrollTimeout.setIfNotSet(() => {
      if (!this._lastMouseEvent) {
        return;
      }
      const position = this._findMousePosition(this._lastMouseEvent, false);
      if (!position) {
        return;
      }
      if (this._mouseState.isDragAndDrop) {
        return;
      }
      this._dispatchMouse(position, true);
    }, 10);
  }
  onCursorStateChanged(e) {
    this._currentSelection = e.selections[0];
  }
  _getPositionOutsideEditor(e) {
    const editorContent = e.editorPos;
    const model = this._context.viewModel;
    const viewLayout = this._context.viewLayout;
    const mouseColumn = this._getMouseColumn(e);
    if (e.posy < editorContent.y) {
      const verticalOffset = Math.max(viewLayout.getCurrentScrollTop() - (editorContent.y - e.posy), 0);
      const viewZoneData = HitTestContext.getZoneAtCoord(this._context, verticalOffset);
      if (viewZoneData) {
        const newPosition = this._helpPositionJumpOverViewZone(viewZoneData);
        if (newPosition) {
          return MouseTarget.createOutsideEditor(mouseColumn, newPosition);
        }
      }
      const aboveLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset);
      return MouseTarget.createOutsideEditor(mouseColumn, new Position(aboveLineNumber, 1));
    }
    if (e.posy > editorContent.y + editorContent.height) {
      const verticalOffset = viewLayout.getCurrentScrollTop() + e.relativePos.y;
      const viewZoneData = HitTestContext.getZoneAtCoord(this._context, verticalOffset);
      if (viewZoneData) {
        const newPosition = this._helpPositionJumpOverViewZone(viewZoneData);
        if (newPosition) {
          return MouseTarget.createOutsideEditor(mouseColumn, newPosition);
        }
      }
      const belowLineNumber = viewLayout.getLineNumberAtVerticalOffset(verticalOffset);
      return MouseTarget.createOutsideEditor(mouseColumn, new Position(belowLineNumber, model.getLineMaxColumn(belowLineNumber)));
    }
    const possibleLineNumber = viewLayout.getLineNumberAtVerticalOffset(viewLayout.getCurrentScrollTop() + e.relativePos.y);
    if (e.posx < editorContent.x) {
      return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, 1));
    }
    if (e.posx > editorContent.x + editorContent.width) {
      return MouseTarget.createOutsideEditor(mouseColumn, new Position(possibleLineNumber, model.getLineMaxColumn(possibleLineNumber)));
    }
    return null;
  }
  _findMousePosition(e, testEventTarget) {
    const positionOutsideEditor = this._getPositionOutsideEditor(e);
    if (positionOutsideEditor) {
      return positionOutsideEditor;
    }
    const t = this._createMouseTarget(e, testEventTarget);
    const hintedPosition = t.position;
    if (!hintedPosition) {
      return null;
    }
    if (t.type === 8 || t.type === 5) {
      const newPosition = this._helpPositionJumpOverViewZone(t.detail);
      if (newPosition) {
        return MouseTarget.createViewZone(t.type, t.element, t.mouseColumn, newPosition, t.detail);
      }
    }
    return t;
  }
  _helpPositionJumpOverViewZone(viewZoneData) {
    const selectionStart = new Position(this._currentSelection.selectionStartLineNumber, this._currentSelection.selectionStartColumn);
    const positionBefore = viewZoneData.positionBefore;
    const positionAfter = viewZoneData.positionAfter;
    if (positionBefore && positionAfter) {
      if (positionBefore.isBefore(selectionStart)) {
        return positionBefore;
      } else {
        return positionAfter;
      }
    }
    return null;
  }
  _dispatchMouse(position, inSelectionMode) {
    if (!position.position) {
      return;
    }
    this._viewController.dispatchMouse({
      position: position.position,
      mouseColumn: position.mouseColumn,
      startedOnLineNumbers: this._mouseState.startedOnLineNumbers,
      inSelectionMode,
      mouseDownCount: this._mouseState.count,
      altKey: this._mouseState.altKey,
      ctrlKey: this._mouseState.ctrlKey,
      metaKey: this._mouseState.metaKey,
      shiftKey: this._mouseState.shiftKey,
      leftButton: this._mouseState.leftButton,
      middleButton: this._mouseState.middleButton,
      onInjectedText: position.type === 6 && position.detail.injectedText !== null
    });
  }
}
class MouseDownState {
  constructor() {
    this._altKey = false;
    this._ctrlKey = false;
    this._metaKey = false;
    this._shiftKey = false;
    this._leftButton = false;
    this._middleButton = false;
    this._startedOnLineNumbers = false;
    this._lastMouseDownPosition = null;
    this._lastMouseDownPositionEqualCount = 0;
    this._lastMouseDownCount = 0;
    this._lastSetMouseDownCountTime = 0;
    this.isDragAndDrop = false;
  }
  get altKey() {
    return this._altKey;
  }
  get ctrlKey() {
    return this._ctrlKey;
  }
  get metaKey() {
    return this._metaKey;
  }
  get shiftKey() {
    return this._shiftKey;
  }
  get leftButton() {
    return this._leftButton;
  }
  get middleButton() {
    return this._middleButton;
  }
  get startedOnLineNumbers() {
    return this._startedOnLineNumbers;
  }
  get count() {
    return this._lastMouseDownCount;
  }
  setModifiers(source) {
    this._altKey = source.altKey;
    this._ctrlKey = source.ctrlKey;
    this._metaKey = source.metaKey;
    this._shiftKey = source.shiftKey;
  }
  setStartButtons(source) {
    this._leftButton = source.leftButton;
    this._middleButton = source.middleButton;
  }
  setStartedOnLineNumbers(startedOnLineNumbers) {
    this._startedOnLineNumbers = startedOnLineNumbers;
  }
  trySetCount(setMouseDownCount, newMouseDownPosition) {
    const currentTime = (/* @__PURE__ */ new Date()).getTime();
    if (currentTime - this._lastSetMouseDownCountTime > MouseDownState.CLEAR_MOUSE_DOWN_COUNT_TIME) {
      setMouseDownCount = 1;
    }
    this._lastSetMouseDownCountTime = currentTime;
    if (setMouseDownCount > this._lastMouseDownCount + 1) {
      setMouseDownCount = this._lastMouseDownCount + 1;
    }
    if (this._lastMouseDownPosition && this._lastMouseDownPosition.equals(newMouseDownPosition)) {
      this._lastMouseDownPositionEqualCount++;
    } else {
      this._lastMouseDownPositionEqualCount = 1;
    }
    this._lastMouseDownPosition = newMouseDownPosition;
    this._lastMouseDownCount = Math.min(setMouseDownCount, this._lastMouseDownPositionEqualCount);
  }
}
MouseDownState.CLEAR_MOUSE_DOWN_COUNT_TIME = 400;
export {
  MouseHandler,
  createMouseMoveEventMerger
};
