import { getDomNodePagePosition, addDisposableListener, addDisposableNonBubblingMouseOutListener, addDisposableThrottledListener, addStandardDisposableListener, isInShadowDOM, createStyleSheet, StandardWindow, addDisposableNonBubblingPointerOutListener } from "../../base/browser/dom.mjs";
import { GlobalMouseMoveMonitor } from "../../base/browser/globalMouseMoveMonitor.mjs";
import { StandardMouseEvent } from "../../base/browser/mouseEvent.mjs";
import { RunOnceScheduler } from "../../base/common/async.mjs";
import { Disposable } from "../../base/common/lifecycle.mjs";
import { asCssVariableName } from "../../platform/theme/common/colorRegistry.mjs";
class PageCoordinates {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this._pageCoordinatesBrand = void 0;
  }
  toClientCoordinates() {
    return new ClientCoordinates(this.x - StandardWindow.scrollX, this.y - StandardWindow.scrollY);
  }
}
class ClientCoordinates {
  constructor(clientX, clientY) {
    this.clientX = clientX;
    this.clientY = clientY;
    this._clientCoordinatesBrand = void 0;
  }
  toPageCoordinates() {
    return new PageCoordinates(this.clientX + StandardWindow.scrollX, this.clientY + StandardWindow.scrollY);
  }
}
class EditorPagePosition {
  constructor(x, y, width, height) {
    this.x = x;
    this.y = y;
    this.width = width;
    this.height = height;
    this._editorPagePositionBrand = void 0;
  }
}
class CoordinatesRelativeToEditor {
  constructor(x, y) {
    this.x = x;
    this.y = y;
    this._positionRelativeToEditorBrand = void 0;
  }
}
function createEditorPagePosition(editorViewDomNode) {
  const editorPos = getDomNodePagePosition(editorViewDomNode);
  return new EditorPagePosition(editorPos.left, editorPos.top, editorPos.width, editorPos.height);
}
function createCoordinatesRelativeToEditor(editorViewDomNode, editorPagePosition, pos) {
  const scaleX = editorPagePosition.width / editorViewDomNode.offsetWidth;
  const scaleY = editorPagePosition.height / editorViewDomNode.offsetHeight;
  const relativeX = (pos.x - editorPagePosition.x) / scaleX;
  const relativeY = (pos.y - editorPagePosition.y) / scaleY;
  return new CoordinatesRelativeToEditor(relativeX, relativeY);
}
class EditorMouseEvent extends StandardMouseEvent {
  constructor(e, editorViewDomNode) {
    super(e);
    this._editorMouseEventBrand = void 0;
    this.pos = new PageCoordinates(this.posx, this.posy);
    this.editorPos = createEditorPagePosition(editorViewDomNode);
    this.relativePos = createCoordinatesRelativeToEditor(editorViewDomNode, this.editorPos, this.pos);
  }
}
class EditorMouseEventFactory {
  constructor(editorViewDomNode) {
    this._editorViewDomNode = editorViewDomNode;
  }
  _create(e) {
    return new EditorMouseEvent(e, this._editorViewDomNode);
  }
  onContextMenu(target, callback) {
    return addDisposableListener(target, "contextmenu", (e) => {
      callback(this._create(e));
    });
  }
  onMouseUp(target, callback) {
    return addDisposableListener(target, "mouseup", (e) => {
      callback(this._create(e));
    });
  }
  onMouseDown(target, callback) {
    return addDisposableListener(target, "mousedown", (e) => {
      callback(this._create(e));
    });
  }
  onMouseLeave(target, callback) {
    return addDisposableNonBubblingMouseOutListener(target, (e) => {
      callback(this._create(e));
    });
  }
  onMouseMoveThrottled(target, callback, merger, minimumTimeMs) {
    const myMerger = (lastEvent, currentEvent) => {
      return merger(lastEvent, this._create(currentEvent));
    };
    return addDisposableThrottledListener(target, "mousemove", callback, myMerger, minimumTimeMs);
  }
}
class EditorPointerEventFactory {
  constructor(editorViewDomNode) {
    this._editorViewDomNode = editorViewDomNode;
  }
  _create(e) {
    return new EditorMouseEvent(e, this._editorViewDomNode);
  }
  onPointerUp(target, callback) {
    return addDisposableListener(target, "pointerup", (e) => {
      callback(this._create(e));
    });
  }
  onPointerDown(target, callback) {
    return addDisposableListener(target, "pointerdown", (e) => {
      callback(this._create(e));
    });
  }
  onPointerLeave(target, callback) {
    return addDisposableNonBubblingPointerOutListener(target, (e) => {
      callback(this._create(e));
    });
  }
  onPointerMoveThrottled(target, callback, merger, minimumTimeMs) {
    const myMerger = (lastEvent, currentEvent) => {
      return merger(lastEvent, this._create(currentEvent));
    };
    return addDisposableThrottledListener(target, "pointermove", callback, myMerger, minimumTimeMs);
  }
}
class GlobalEditorMouseMoveMonitor extends Disposable {
  constructor(editorViewDomNode) {
    super();
    this._editorViewDomNode = editorViewDomNode;
    this._globalMouseMoveMonitor = this._register(new GlobalMouseMoveMonitor());
    this._keydownListener = null;
  }
  startMonitoring(initialElement, initialButtons, merger, mouseMoveCallback, onStopCallback) {
    this._keydownListener = addStandardDisposableListener(document, "keydown", (e) => {
      const kb = e.toKeybinding();
      if (kb.isModifierKey()) {
        return;
      }
      this._globalMouseMoveMonitor.stopMonitoring(true, e.browserEvent);
    }, true);
    const myMerger = (lastEvent, currentEvent) => {
      return merger(lastEvent, new EditorMouseEvent(currentEvent, this._editorViewDomNode));
    };
    this._globalMouseMoveMonitor.startMonitoring(initialElement, initialButtons, myMerger, mouseMoveCallback, (e) => {
      this._keydownListener.dispose();
      onStopCallback(e);
    });
  }
  stopMonitoring() {
    this._globalMouseMoveMonitor.stopMonitoring(true);
  }
}
class DynamicCssRules {
  constructor(_editor) {
    this._editor = _editor;
    this._instanceId = ++DynamicCssRules._idPool;
    this._counter = 0;
    this._rules = /* @__PURE__ */ new Map();
    this._garbageCollectionScheduler = new RunOnceScheduler(() => this.garbageCollect(), 1e3);
  }
  createClassNameRef(options) {
    const rule = this.getOrCreateRule(options);
    rule.increaseRefCount();
    return {
      className: rule.className,
      dispose: () => {
        rule.decreaseRefCount();
        this._garbageCollectionScheduler.schedule();
      }
    };
  }
  getOrCreateRule(properties) {
    const key = this.computeUniqueKey(properties);
    let existingRule = this._rules.get(key);
    if (!existingRule) {
      const counter = this._counter++;
      existingRule = new RefCountedCssRule(key, `dyn-rule-${this._instanceId}-${counter}`, isInShadowDOM(this._editor.getContainerDomNode()) ? this._editor.getContainerDomNode() : void 0, properties);
      this._rules.set(key, existingRule);
    }
    return existingRule;
  }
  computeUniqueKey(properties) {
    return JSON.stringify(properties);
  }
  garbageCollect() {
    for (const rule of this._rules.values()) {
      if (!rule.hasReferences()) {
        this._rules.delete(rule.key);
        rule.dispose();
      }
    }
  }
}
DynamicCssRules._idPool = 0;
class RefCountedCssRule {
  constructor(key, className, _containerElement, properties) {
    this.key = key;
    this.className = className;
    this.properties = properties;
    this._referenceCount = 0;
    this._styleElement = createStyleSheet(_containerElement);
    this._styleElement.textContent = this.getCssText(this.className, this.properties);
  }
  getCssText(className, properties) {
    let str = `.${className} {`;
    for (const prop in properties) {
      const value = properties[prop];
      let cssValue;
      if (typeof value === "object") {
        cssValue = `var(${asCssVariableName(value.id)})`;
      } else {
        cssValue = value;
      }
      const cssPropName = camelToDashes(prop);
      str += `
	${cssPropName}: ${cssValue};`;
    }
    str += `
}`;
    return str;
  }
  dispose() {
    this._styleElement.remove();
  }
  increaseRefCount() {
    this._referenceCount++;
  }
  decreaseRefCount() {
    this._referenceCount--;
  }
  hasReferences() {
    return this._referenceCount > 0;
  }
}
function camelToDashes(str) {
  return str.replace(/(^[A-Z])/, ([first]) => first.toLowerCase()).replace(/([A-Z])/g, ([letter]) => `-${letter.toLowerCase()}`);
}
export {
  ClientCoordinates,
  CoordinatesRelativeToEditor,
  DynamicCssRules,
  EditorMouseEvent,
  EditorMouseEventFactory,
  EditorPagePosition,
  EditorPointerEventFactory,
  GlobalEditorMouseMoveMonitor,
  PageCoordinates,
  createCoordinatesRelativeToEditor,
  createEditorPagePosition
};
