import { isWebKit, isFirefox, isSafari } from "../../../../base/browser/browser.mjs";
import { createFastDomNode } from "../../../../base/browser/fastDomNode.mjs";
import { isNative, isLinux } from "../../../../base/common/platform.mjs";
import { RangeUtil } from "./rangeUtil.mjs";
import { VisibleRanges, FloatHorizontalRange } from "../../view/renderingContext.mjs";
import { LineDecoration } from "../../../common/viewLayout/lineDecorations.mjs";
import { LineRange, RenderLineInput, renderViewLine, DomPosition } from "../../../common/viewLayout/viewLineRenderer.mjs";
import { ColorScheme } from "../../../../platform/theme/common/theme.mjs";
import { EditorFontLigatures } from "../../../common/config/editorOptions.mjs";
const canUseFastRenderedViewLine = function() {
  if (isNative) {
    return true;
  }
  if (isLinux || isFirefox || isSafari) {
    return false;
  }
  return true;
}();
let monospaceAssumptionsAreValid = true;
class DomReadingContext {
  constructor(domNode, endNode) {
    this._domNode = domNode;
    this._clientRectDeltaLeft = 0;
    this._clientRectScale = 1;
    this._clientRectRead = false;
    this.endNode = endNode;
  }
  readClientRect() {
    if (!this._clientRectRead) {
      this._clientRectRead = true;
      const rect = this._domNode.getBoundingClientRect();
      this._clientRectDeltaLeft = rect.left;
      this._clientRectScale = rect.width / this._domNode.offsetWidth;
    }
  }
  get clientRectDeltaLeft() {
    if (!this._clientRectRead) {
      this.readClientRect();
    }
    return this._clientRectDeltaLeft;
  }
  get clientRectScale() {
    if (!this._clientRectRead) {
      this.readClientRect();
    }
    return this._clientRectScale;
  }
}
class ViewLineOptions {
  constructor(config, themeType) {
    this.themeType = themeType;
    const options = config.options;
    const fontInfo = options.get(
      44
      /* fontInfo */
    );
    this.renderWhitespace = options.get(
      88
      /* renderWhitespace */
    );
    this.renderControlCharacters = options.get(
      83
      /* renderControlCharacters */
    );
    this.spaceWidth = fontInfo.spaceWidth;
    this.middotWidth = fontInfo.middotWidth;
    this.wsmiddotWidth = fontInfo.wsmiddotWidth;
    this.useMonospaceOptimizations = fontInfo.isMonospace && !options.get(
      29
      /* disableMonospaceOptimizations */
    );
    this.canUseHalfwidthRightwardsArrow = fontInfo.canUseHalfwidthRightwardsArrow;
    this.lineHeight = options.get(
      59
      /* lineHeight */
    );
    this.stopRenderingLineAfter = options.get(
      105
      /* stopRenderingLineAfter */
    );
    this.fontLigatures = options.get(
      45
      /* fontLigatures */
    );
  }
  equals(other) {
    return this.themeType === other.themeType && this.renderWhitespace === other.renderWhitespace && this.renderControlCharacters === other.renderControlCharacters && this.spaceWidth === other.spaceWidth && this.middotWidth === other.middotWidth && this.wsmiddotWidth === other.wsmiddotWidth && this.useMonospaceOptimizations === other.useMonospaceOptimizations && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow && this.lineHeight === other.lineHeight && this.stopRenderingLineAfter === other.stopRenderingLineAfter && this.fontLigatures === other.fontLigatures;
  }
}
class ViewLine {
  constructor(options) {
    this._options = options;
    this._isMaybeInvalid = true;
    this._renderedViewLine = null;
  }
  // --- begin IVisibleLineData
  getDomNode() {
    if (this._renderedViewLine && this._renderedViewLine.domNode) {
      return this._renderedViewLine.domNode.domNode;
    }
    return null;
  }
  setDomNode(domNode) {
    if (this._renderedViewLine) {
      this._renderedViewLine.domNode = createFastDomNode(domNode);
    } else {
      throw new Error("I have no rendered view line to set the dom node to...");
    }
  }
  onContentChanged() {
    this._isMaybeInvalid = true;
  }
  onTokensChanged() {
    this._isMaybeInvalid = true;
  }
  onDecorationsChanged() {
    this._isMaybeInvalid = true;
  }
  onOptionsChanged(newOptions) {
    this._isMaybeInvalid = true;
    this._options = newOptions;
  }
  onSelectionChanged() {
    if (this._options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === "selection") {
      this._isMaybeInvalid = true;
      return true;
    }
    return false;
  }
  renderLine(lineNumber, deltaTop, viewportData, sb) {
    if (this._isMaybeInvalid === false) {
      return false;
    }
    this._isMaybeInvalid = false;
    const lineData = viewportData.getViewLineRenderingData(lineNumber);
    const options = this._options;
    const actualInlineDecorations = LineDecoration.filter(lineData.inlineDecorations, lineNumber, lineData.minColumn, lineData.maxColumn);
    let selectionsOnLine = null;
    if (options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace === "selection") {
      const selections = viewportData.selections;
      for (const selection of selections) {
        if (selection.endLineNumber < lineNumber || selection.startLineNumber > lineNumber) {
          continue;
        }
        const startColumn = selection.startLineNumber === lineNumber ? selection.startColumn : lineData.minColumn;
        const endColumn = selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn;
        if (startColumn < endColumn) {
          if (options.themeType === ColorScheme.HIGH_CONTRAST || this._options.renderWhitespace !== "selection") {
            actualInlineDecorations.push(new LineDecoration(
              startColumn,
              endColumn,
              "inline-selected-text",
              0
              /* Regular */
            ));
          } else {
            if (!selectionsOnLine) {
              selectionsOnLine = [];
            }
            selectionsOnLine.push(new LineRange(startColumn - 1, endColumn - 1));
          }
        }
      }
    }
    const renderLineInput = new RenderLineInput(options.useMonospaceOptimizations, options.canUseHalfwidthRightwardsArrow, lineData.content, lineData.continuesWithWrappedLine, lineData.isBasicASCII, lineData.containsRTL, lineData.minColumn - 1, lineData.tokens, actualInlineDecorations, lineData.tabSize, lineData.startVisibleColumn, options.spaceWidth, options.middotWidth, options.wsmiddotWidth, options.stopRenderingLineAfter, options.renderWhitespace, options.renderControlCharacters, options.fontLigatures !== EditorFontLigatures.OFF, selectionsOnLine);
    if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) {
      return false;
    }
    sb.appendASCIIString('<div style="top:');
    sb.appendASCIIString(String(deltaTop));
    sb.appendASCIIString("px;height:");
    sb.appendASCIIString(String(this._options.lineHeight));
    sb.appendASCIIString('px;" class="');
    sb.appendASCIIString(ViewLine.CLASS_NAME);
    sb.appendASCIIString('">');
    const output = renderViewLine(renderLineInput, sb);
    sb.appendASCIIString("</div>");
    let renderedViewLine = null;
    if (monospaceAssumptionsAreValid && canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === 0) {
      if (lineData.content.length < 300 && renderLineInput.lineTokens.getCount() < 100) {
        renderedViewLine = new FastRenderedViewLine(this._renderedViewLine ? this._renderedViewLine.domNode : null, renderLineInput, output.characterMapping);
      }
    }
    if (!renderedViewLine) {
      renderedViewLine = createRenderedLine(this._renderedViewLine ? this._renderedViewLine.domNode : null, renderLineInput, output.characterMapping, output.containsRTL, output.containsForeignElements);
    }
    this._renderedViewLine = renderedViewLine;
    return true;
  }
  layoutLine(lineNumber, deltaTop) {
    if (this._renderedViewLine && this._renderedViewLine.domNode) {
      this._renderedViewLine.domNode.setTop(deltaTop);
      this._renderedViewLine.domNode.setHeight(this._options.lineHeight);
    }
  }
  // --- end IVisibleLineData
  getWidth() {
    if (!this._renderedViewLine) {
      return 0;
    }
    return this._renderedViewLine.getWidth();
  }
  getWidthIsFast() {
    if (!this._renderedViewLine) {
      return true;
    }
    return this._renderedViewLine.getWidthIsFast();
  }
  needsMonospaceFontCheck() {
    if (!this._renderedViewLine) {
      return false;
    }
    return this._renderedViewLine instanceof FastRenderedViewLine;
  }
  monospaceAssumptionsAreValid() {
    if (!this._renderedViewLine) {
      return monospaceAssumptionsAreValid;
    }
    if (this._renderedViewLine instanceof FastRenderedViewLine) {
      return this._renderedViewLine.monospaceAssumptionsAreValid();
    }
    return monospaceAssumptionsAreValid;
  }
  onMonospaceAssumptionsInvalidated() {
    if (this._renderedViewLine && this._renderedViewLine instanceof FastRenderedViewLine) {
      this._renderedViewLine = this._renderedViewLine.toSlowRenderedLine();
    }
  }
  getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
    if (!this._renderedViewLine) {
      return null;
    }
    startColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, startColumn));
    endColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, endColumn));
    const stopRenderingLineAfter = this._renderedViewLine.input.stopRenderingLineAfter;
    let outsideRenderedLine = false;
    if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1 && endColumn > stopRenderingLineAfter + 1) {
      outsideRenderedLine = true;
    }
    if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1) {
      startColumn = stopRenderingLineAfter + 1;
    }
    if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter + 1) {
      endColumn = stopRenderingLineAfter + 1;
    }
    const horizontalRanges = this._renderedViewLine.getVisibleRangesForRange(lineNumber, startColumn, endColumn, context);
    if (horizontalRanges && horizontalRanges.length > 0) {
      return new VisibleRanges(outsideRenderedLine, horizontalRanges);
    }
    return null;
  }
  getColumnOfNodeOffset(lineNumber, spanNode, offset) {
    if (!this._renderedViewLine) {
      return 1;
    }
    return this._renderedViewLine.getColumnOfNodeOffset(lineNumber, spanNode, offset);
  }
}
ViewLine.CLASS_NAME = "view-line";
class FastRenderedViewLine {
  constructor(domNode, renderLineInput, characterMapping) {
    this.domNode = domNode;
    this.input = renderLineInput;
    this._characterMapping = characterMapping;
    this._charWidth = renderLineInput.spaceWidth;
  }
  getWidth() {
    return Math.round(this._getCharPosition(this._characterMapping.length));
  }
  getWidthIsFast() {
    return true;
  }
  monospaceAssumptionsAreValid() {
    if (!this.domNode) {
      return monospaceAssumptionsAreValid;
    }
    const expectedWidth = this.getWidth();
    const actualWidth = this.domNode.domNode.firstChild.offsetWidth;
    if (Math.abs(expectedWidth - actualWidth) >= 2) {
      console.warn(`monospace assumptions have been violated, therefore disabling monospace optimizations!`);
      monospaceAssumptionsAreValid = false;
    }
    return monospaceAssumptionsAreValid;
  }
  toSlowRenderedLine() {
    return createRenderedLine(
      this.domNode,
      this.input,
      this._characterMapping,
      false,
      0
      /* None */
    );
  }
  getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
    const startPosition = this._getCharPosition(startColumn);
    const endPosition = this._getCharPosition(endColumn);
    return [new FloatHorizontalRange(startPosition, endPosition - startPosition)];
  }
  _getCharPosition(column) {
    const charOffset = this._characterMapping.getAbsoluteOffset(column);
    return this._charWidth * charOffset;
  }
  getColumnOfNodeOffset(lineNumber, spanNode, offset) {
    const spanNodeTextContentLength = spanNode.textContent.length;
    let spanIndex = -1;
    while (spanNode) {
      spanNode = spanNode.previousSibling;
      spanIndex++;
    }
    return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength);
  }
}
class RenderedViewLine {
  constructor(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
    this.domNode = domNode;
    this.input = renderLineInput;
    this._characterMapping = characterMapping;
    this._isWhitespaceOnly = /^\s*$/.test(renderLineInput.lineContent);
    this._containsForeignElements = containsForeignElements;
    this._cachedWidth = -1;
    this._pixelOffsetCache = null;
    if (!containsRTL || this._characterMapping.length === 0) {
      this._pixelOffsetCache = new Float32Array(Math.max(2, this._characterMapping.length + 1));
      for (let column = 0, len = this._characterMapping.length; column <= len; column++) {
        this._pixelOffsetCache[column] = -1;
      }
    }
  }
  // --- Reading from the DOM methods
  _getReadingTarget(myDomNode) {
    return myDomNode.domNode.firstChild;
  }
  /**
   * Width of the line in pixels
   */
  getWidth() {
    if (!this.domNode) {
      return 0;
    }
    if (this._cachedWidth === -1) {
      this._cachedWidth = this._getReadingTarget(this.domNode).offsetWidth;
    }
    return this._cachedWidth;
  }
  getWidthIsFast() {
    if (this._cachedWidth === -1) {
      return false;
    }
    return true;
  }
  /**
   * Visible ranges for a model range
   */
  getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
    if (!this.domNode) {
      return null;
    }
    if (this._pixelOffsetCache !== null) {
      const startOffset = this._readPixelOffset(this.domNode, lineNumber, startColumn, context);
      if (startOffset === -1) {
        return null;
      }
      const endOffset = this._readPixelOffset(this.domNode, lineNumber, endColumn, context);
      if (endOffset === -1) {
        return null;
      }
      return [new FloatHorizontalRange(startOffset, endOffset - startOffset)];
    }
    return this._readVisibleRangesForRange(this.domNode, lineNumber, startColumn, endColumn, context);
  }
  _readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context) {
    if (startColumn === endColumn) {
      const pixelOffset = this._readPixelOffset(domNode, lineNumber, startColumn, context);
      if (pixelOffset === -1) {
        return null;
      } else {
        return [new FloatHorizontalRange(pixelOffset, 0)];
      }
    } else {
      return this._readRawVisibleRangesForRange(domNode, startColumn, endColumn, context);
    }
  }
  _readPixelOffset(domNode, lineNumber, column, context) {
    if (this._characterMapping.length === 0) {
      if (this._containsForeignElements === 0) {
        return 0;
      }
      if (this._containsForeignElements === 2) {
        return 0;
      }
      if (this._containsForeignElements === 1) {
        return this.getWidth();
      }
      const readingTarget = this._getReadingTarget(domNode);
      if (readingTarget.firstChild) {
        return readingTarget.firstChild.offsetWidth;
      } else {
        return 0;
      }
    }
    if (this._pixelOffsetCache !== null) {
      const cachedPixelOffset = this._pixelOffsetCache[column];
      if (cachedPixelOffset !== -1) {
        return cachedPixelOffset;
      }
      const result = this._actualReadPixelOffset(domNode, lineNumber, column, context);
      this._pixelOffsetCache[column] = result;
      return result;
    }
    return this._actualReadPixelOffset(domNode, lineNumber, column, context);
  }
  _actualReadPixelOffset(domNode, lineNumber, column, context) {
    if (this._characterMapping.length === 0) {
      const r2 = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), 0, 0, 0, 0, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
      if (!r2 || r2.length === 0) {
        return -1;
      }
      return r2[0].left;
    }
    if (column === this._characterMapping.length && this._isWhitespaceOnly && this._containsForeignElements === 0) {
      return this.getWidth();
    }
    const domPosition = this._characterMapping.getDomPosition(column);
    const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), domPosition.partIndex, domPosition.charIndex, domPosition.partIndex, domPosition.charIndex, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
    if (!r || r.length === 0) {
      return -1;
    }
    const result = r[0].left;
    if (this.input.isBasicASCII) {
      const charOffset = this._characterMapping.getAbsoluteOffset(column);
      const expectedResult = Math.round(this.input.spaceWidth * charOffset);
      if (Math.abs(expectedResult - result) <= 1) {
        return expectedResult;
      }
    }
    return result;
  }
  _readRawVisibleRangesForRange(domNode, startColumn, endColumn, context) {
    if (startColumn === 1 && endColumn === this._characterMapping.length) {
      return [new FloatHorizontalRange(0, this.getWidth())];
    }
    const startDomPosition = this._characterMapping.getDomPosition(startColumn);
    const endDomPosition = this._characterMapping.getDomPosition(endColumn);
    return RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), startDomPosition.partIndex, startDomPosition.charIndex, endDomPosition.partIndex, endDomPosition.charIndex, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
  }
  /**
   * Returns the column for the text found at a specific offset inside a rendered dom node
   */
  getColumnOfNodeOffset(lineNumber, spanNode, offset) {
    const spanNodeTextContentLength = spanNode.textContent.length;
    let spanIndex = -1;
    while (spanNode) {
      spanNode = spanNode.previousSibling;
      spanIndex++;
    }
    return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength);
  }
}
class WebKitRenderedViewLine extends RenderedViewLine {
  _readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context) {
    const output = super._readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context);
    if (!output || output.length === 0 || startColumn === endColumn || startColumn === 1 && endColumn === this._characterMapping.length) {
      return output;
    }
    if (!this.input.containsRTL) {
      const endPixelOffset = this._readPixelOffset(domNode, lineNumber, endColumn, context);
      if (endPixelOffset !== -1) {
        const lastRange = output[output.length - 1];
        if (lastRange.left < endPixelOffset) {
          lastRange.width = endPixelOffset - lastRange.left;
        }
      }
    }
    return output;
  }
}
const createRenderedLine = function() {
  if (isWebKit) {
    return createWebKitRenderedLine;
  }
  return createNormalRenderedLine;
}();
function createWebKitRenderedLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
  return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
}
function createNormalRenderedLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
  return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
}
export {
  DomReadingContext,
  ViewLine,
  ViewLineOptions
};
