'use strict';

require('../core/settings.js');
var color = require('../color.js');
var constants = require('../constants.js');
require('../extensions.js');
require('../math/const.js');
var Point = require('../math/Point.js');
require('../math/groupD8.js');
var ObservablePoint = require('../math/ObservablePoint.js');
require('../math/Transform.js');
require('../runner.js');
var settings = require('../settings/settings.js');
require('../ticker/settings.js');
require('../ticker/const.js');
require('../ticker/Ticker.js');
require('../ticker/TickerPlugin.js');
require('../utils/settings.js');
require('../utils/is_mobile/index.js');
require('../utils/color/premultiply.js');
var removeItems = require('../utils/data/removeItems.js');
require('../utils/media/BoundingBox.js');
require('../core/autoDetectRenderer.js');
require('../core/background/BackgroundSystem.js');
require('../core/batch/BatchRenderer.js');
var Program = require('../core/shader/Program.js');
require('../core/batch/BatchSystem.js');
require('../core/context/ContextSystem.js');
require('../core/filters/Filter.js');
require('../core/filters/FilterSystem.js');
require('../core/textures/BaseTexture.js');
require('../core/framebuffer/FramebufferSystem.js');
require('../core/framebuffer/MultisampleSystem.js');
require('../core/geometry/BufferSystem.js');
require('../core/geometry/GeometrySystem.js');
require('../core/mask/MaskSystem.js');
require('../core/mask/ScissorSystem.js');
require('../core/mask/StencilSystem.js');
require('../core/plugin/PluginSystem.js');
require('../core/projection/ProjectionSystem.js');
require('../core/render/ObjectRendererSystem.js');
require('../core/Renderer.js');
require('../core/renderTexture/GenerateTextureSystem.js');
var Texture = require('../core/textures/Texture.js');
require('../core/renderTexture/RenderTexturePool.js');
require('../core/renderTexture/RenderTextureSystem.js');
require('../core/shader/ShaderSystem.js');
require('../core/startup/StartupSystem.js');
require('../core/state/StateSystem.js');
require('../core/textures/TextureGCSystem.js');
require('../core/textures/TextureSystem.js');
require('../core/transformFeedback/TransformFeedbackSystem.js');
require('../core/view/ViewSystem.js');
require('../core/textures/resources/index.js');
require('../display/settings.js');
var Container = require('../display/Container.js');
require('../display/DisplayObject.js');
var Mesh = require('../mesh/Mesh.js');
var MeshGeometry = require('../mesh/MeshGeometry.js');
var MeshMaterial = require('../mesh/MeshMaterial.js');
var BitmapFont = require('./BitmapFont.js');
var msdf$1 = require('./shader/msdf.frag.js');
var msdf = require('./shader/msdf.vert.js');
require('../text/const.js');
require('../text/Text.js');
require('../text/TextMetrics.js');
require('../text/TextStyle.js');
var extractCharCode = require('./utils/extractCharCode.js');
var splitTextToCharacters = require('./utils/splitTextToCharacters.js');

const pageMeshDataDefaultPageMeshData = [];
const pageMeshDataMSDFPageMeshData = [];
const charRenderDataPool = [];
class BitmapText extends Container.Container {
  /**
   * @param text - A string that you would like the text to display.
   * @param style - The style parameters.
   * @param {string} style.fontName - The installed BitmapFont name.
   * @param {number} [style.fontSize] - The size of the font in pixels, e.g. 24. If undefined,
   *.     this will default to the BitmapFont size.
   * @param {string} [style.align='left'] - Alignment for multiline text ('left', 'center', 'right' or 'justify'),
   *      does not affect single line text.
   * @param {PIXI.ColorSource} [style.tint=0xFFFFFF] - The tint color.
   * @param {number} [style.letterSpacing=0] - The amount of spacing between letters.
   * @param {number} [style.maxWidth=0] - The max width of the text before line wrapping.
   */
  constructor(text, style = {}) {
    super();
    const { align, tint, maxWidth, letterSpacing, fontName, fontSize } = Object.assign({}, BitmapText.styleDefaults, style);
    if (!BitmapFont.BitmapFont.available[fontName]) {
      throw new Error(`Missing BitmapFont "${fontName}"`);
    }
    this._activePagesMeshData = [];
    this._textWidth = 0;
    this._textHeight = 0;
    this._align = align;
    this._tintColor = new color.Color(tint);
    this._font = void 0;
    this._fontName = fontName;
    this._fontSize = fontSize;
    this.text = text;
    this._maxWidth = maxWidth;
    this._maxLineHeight = 0;
    this._letterSpacing = letterSpacing;
    this._anchor = new ObservablePoint.ObservablePoint(() => {
      this.dirty = true;
    }, this, 0, 0);
    this._roundPixels = settings.settings.ROUND_PIXELS;
    this.dirty = true;
    this._resolution = settings.settings.RESOLUTION;
    this._autoResolution = true;
    this._textureCache = {};
  }
  /** Renders text and updates it when needed. This should only be called if the BitmapFont is regenerated. */
  updateText() {
    const data = BitmapFont.BitmapFont.available[this._fontName];
    const fontSize = this.fontSize;
    const scale = fontSize / data.size;
    const pos = new Point.Point();
    const chars = [];
    const lineWidths = [];
    const lineSpaces = [];
    const text = this._text.replace(/(?:\r\n|\r)/g, "\n") || " ";
    const charsInput = splitTextToCharacters.splitTextToCharacters(text);
    const maxWidth = this._maxWidth * data.size / fontSize;
    const pageMeshDataPool = data.distanceFieldType === "none" ? pageMeshDataDefaultPageMeshData : pageMeshDataMSDFPageMeshData;
    let prevCharCode = null;
    let lastLineWidth = 0;
    let maxLineWidth = 0;
    let line = 0;
    let lastBreakPos = -1;
    let lastBreakWidth = 0;
    let spacesRemoved = 0;
    let maxLineHeight = 0;
    let spaceCount = 0;
    for (let i = 0; i < charsInput.length; i++) {
      const char = charsInput[i];
      const charCode = extractCharCode.extractCharCode(char);
      if (/(?:\s)/.test(char)) {
        lastBreakPos = i;
        lastBreakWidth = lastLineWidth;
        spaceCount++;
      }
      if (char === "\r" || char === "\n") {
        lineWidths.push(lastLineWidth);
        lineSpaces.push(-1);
        maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
        ++line;
        ++spacesRemoved;
        pos.x = 0;
        pos.y += data.lineHeight;
        prevCharCode = null;
        spaceCount = 0;
        continue;
      }
      const charData = data.chars[charCode];
      if (!charData) {
        continue;
      }
      if (prevCharCode && charData.kerning[prevCharCode]) {
        pos.x += charData.kerning[prevCharCode];
      }
      const charRenderData = charRenderDataPool.pop() || {
        texture: Texture.Texture.EMPTY,
        line: 0,
        charCode: 0,
        prevSpaces: 0,
        position: new Point.Point()
      };
      charRenderData.texture = charData.texture;
      charRenderData.line = line;
      charRenderData.charCode = charCode;
      charRenderData.position.x = Math.round(pos.x + charData.xOffset + this._letterSpacing / 2);
      charRenderData.position.y = Math.round(pos.y + charData.yOffset);
      charRenderData.prevSpaces = spaceCount;
      chars.push(charRenderData);
      lastLineWidth = charRenderData.position.x + Math.max(charData.xAdvance - charData.xOffset, charData.texture.orig.width);
      pos.x += charData.xAdvance + this._letterSpacing;
      maxLineHeight = Math.max(maxLineHeight, charData.yOffset + charData.texture.height);
      prevCharCode = charCode;
      if (lastBreakPos !== -1 && maxWidth > 0 && pos.x > maxWidth) {
        ++spacesRemoved;
        removeItems.removeItems(chars, 1 + lastBreakPos - spacesRemoved, 1 + i - lastBreakPos);
        i = lastBreakPos;
        lastBreakPos = -1;
        lineWidths.push(lastBreakWidth);
        lineSpaces.push(chars.length > 0 ? chars[chars.length - 1].prevSpaces : 0);
        maxLineWidth = Math.max(maxLineWidth, lastBreakWidth);
        line++;
        pos.x = 0;
        pos.y += data.lineHeight;
        prevCharCode = null;
        spaceCount = 0;
      }
    }
    const lastChar = charsInput[charsInput.length - 1];
    if (lastChar !== "\r" && lastChar !== "\n") {
      if (/(?:\s)/.test(lastChar)) {
        lastLineWidth = lastBreakWidth;
      }
      lineWidths.push(lastLineWidth);
      maxLineWidth = Math.max(maxLineWidth, lastLineWidth);
      lineSpaces.push(-1);
    }
    const lineAlignOffsets = [];
    for (let i = 0; i <= line; i++) {
      let alignOffset = 0;
      if (this._align === "right") {
        alignOffset = maxLineWidth - lineWidths[i];
      } else if (this._align === "center") {
        alignOffset = (maxLineWidth - lineWidths[i]) / 2;
      } else if (this._align === "justify") {
        alignOffset = lineSpaces[i] < 0 ? 0 : (maxLineWidth - lineWidths[i]) / lineSpaces[i];
      }
      lineAlignOffsets.push(alignOffset);
    }
    const lenChars = chars.length;
    const pagesMeshData = {};
    const newPagesMeshData = [];
    const activePagesMeshData = this._activePagesMeshData;
    pageMeshDataPool.push(...activePagesMeshData);
    for (let i = 0; i < lenChars; i++) {
      const texture = chars[i].texture;
      const baseTextureUid = texture.baseTexture.uid;
      if (!pagesMeshData[baseTextureUid]) {
        let pageMeshData = pageMeshDataPool.pop();
        if (!pageMeshData) {
          const geometry = new MeshGeometry.MeshGeometry();
          let material;
          let meshBlendMode;
          if (data.distanceFieldType === "none") {
            material = new MeshMaterial.MeshMaterial(Texture.Texture.EMPTY);
            meshBlendMode = constants.BLEND_MODES.NORMAL;
          } else {
            material = new MeshMaterial.MeshMaterial(Texture.Texture.EMPTY, { program: Program.Program.from(msdf.default, msdf$1.default), uniforms: { uFWidth: 0 } });
            meshBlendMode = constants.BLEND_MODES.NORMAL_NPM;
          }
          const mesh = new Mesh.Mesh(geometry, material);
          mesh.blendMode = meshBlendMode;
          pageMeshData = {
            index: 0,
            indexCount: 0,
            vertexCount: 0,
            uvsCount: 0,
            total: 0,
            mesh,
            vertices: null,
            uvs: null,
            indices: null
          };
        }
        pageMeshData.index = 0;
        pageMeshData.indexCount = 0;
        pageMeshData.vertexCount = 0;
        pageMeshData.uvsCount = 0;
        pageMeshData.total = 0;
        const { _textureCache } = this;
        _textureCache[baseTextureUid] = _textureCache[baseTextureUid] || new Texture.Texture(texture.baseTexture);
        pageMeshData.mesh.texture = _textureCache[baseTextureUid];
        pageMeshData.mesh.tint = this._tintColor.value;
        newPagesMeshData.push(pageMeshData);
        pagesMeshData[baseTextureUid] = pageMeshData;
      }
      pagesMeshData[baseTextureUid].total++;
    }
    for (let i = 0; i < activePagesMeshData.length; i++) {
      if (!newPagesMeshData.includes(activePagesMeshData[i])) {
        this.removeChild(activePagesMeshData[i].mesh);
      }
    }
    for (let i = 0; i < newPagesMeshData.length; i++) {
      if (newPagesMeshData[i].mesh.parent !== this) {
        this.addChild(newPagesMeshData[i].mesh);
      }
    }
    this._activePagesMeshData = newPagesMeshData;
    for (const i in pagesMeshData) {
      const pageMeshData = pagesMeshData[i];
      const total = pageMeshData.total;
      if (!(pageMeshData.indices?.length > 6 * total) || pageMeshData.vertices.length < Mesh.Mesh.BATCHABLE_SIZE * 2) {
        pageMeshData.vertices = new Float32Array(4 * 2 * total);
        pageMeshData.uvs = new Float32Array(4 * 2 * total);
        pageMeshData.indices = new Uint16Array(6 * total);
      } else {
        const total2 = pageMeshData.total;
        const vertices = pageMeshData.vertices;
        for (let i2 = total2 * 4 * 2; i2 < vertices.length; i2++) {
          vertices[i2] = 0;
        }
      }
      pageMeshData.mesh.size = 6 * total;
    }
    for (let i = 0; i < lenChars; i++) {
      const char = chars[i];
      let offset = char.position.x + lineAlignOffsets[char.line] * (this._align === "justify" ? char.prevSpaces : 1);
      if (this._roundPixels) {
        offset = Math.round(offset);
      }
      const xPos = offset * scale;
      const yPos = char.position.y * scale;
      const texture = char.texture;
      const pageMesh = pagesMeshData[texture.baseTexture.uid];
      const textureFrame = texture.frame;
      const textureUvs = texture._uvs;
      const index = pageMesh.index++;
      pageMesh.indices[index * 6 + 0] = 0 + index * 4;
      pageMesh.indices[index * 6 + 1] = 1 + index * 4;
      pageMesh.indices[index * 6 + 2] = 2 + index * 4;
      pageMesh.indices[index * 6 + 3] = 0 + index * 4;
      pageMesh.indices[index * 6 + 4] = 2 + index * 4;
      pageMesh.indices[index * 6 + 5] = 3 + index * 4;
      pageMesh.vertices[index * 8 + 0] = xPos;
      pageMesh.vertices[index * 8 + 1] = yPos;
      pageMesh.vertices[index * 8 + 2] = xPos + textureFrame.width * scale;
      pageMesh.vertices[index * 8 + 3] = yPos;
      pageMesh.vertices[index * 8 + 4] = xPos + textureFrame.width * scale;
      pageMesh.vertices[index * 8 + 5] = yPos + textureFrame.height * scale;
      pageMesh.vertices[index * 8 + 6] = xPos;
      pageMesh.vertices[index * 8 + 7] = yPos + textureFrame.height * scale;
      pageMesh.uvs[index * 8 + 0] = textureUvs.x0;
      pageMesh.uvs[index * 8 + 1] = textureUvs.y0;
      pageMesh.uvs[index * 8 + 2] = textureUvs.x1;
      pageMesh.uvs[index * 8 + 3] = textureUvs.y1;
      pageMesh.uvs[index * 8 + 4] = textureUvs.x2;
      pageMesh.uvs[index * 8 + 5] = textureUvs.y2;
      pageMesh.uvs[index * 8 + 6] = textureUvs.x3;
      pageMesh.uvs[index * 8 + 7] = textureUvs.y3;
    }
    this._textWidth = maxLineWidth * scale;
    this._textHeight = (pos.y + data.lineHeight) * scale;
    for (const i in pagesMeshData) {
      const pageMeshData = pagesMeshData[i];
      if (this.anchor.x !== 0 || this.anchor.y !== 0) {
        let vertexCount = 0;
        const anchorOffsetX = this._textWidth * this.anchor.x;
        const anchorOffsetY = this._textHeight * this.anchor.y;
        for (let i2 = 0; i2 < pageMeshData.total; i2++) {
          pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetX;
          pageMeshData.vertices[vertexCount++] -= anchorOffsetY;
        }
      }
      this._maxLineHeight = maxLineHeight * scale;
      const vertexBuffer = pageMeshData.mesh.geometry.getBuffer("aVertexPosition");
      const textureBuffer = pageMeshData.mesh.geometry.getBuffer("aTextureCoord");
      const indexBuffer = pageMeshData.mesh.geometry.getIndex();
      vertexBuffer.data = pageMeshData.vertices;
      textureBuffer.data = pageMeshData.uvs;
      indexBuffer.data = pageMeshData.indices;
      vertexBuffer.update();
      textureBuffer.update();
      indexBuffer.update();
    }
    for (let i = 0; i < chars.length; i++) {
      charRenderDataPool.push(chars[i]);
    }
    this._font = data;
    this.dirty = false;
  }
  updateTransform() {
    this.validate();
    this.containerUpdateTransform();
  }
  _render(renderer) {
    if (this._autoResolution && this._resolution !== renderer.resolution) {
      this._resolution = renderer.resolution;
      this.dirty = true;
    }
    const { distanceFieldRange, distanceFieldType, size } = BitmapFont.BitmapFont.available[this._fontName];
    if (distanceFieldType !== "none") {
      const { a, b, c, d } = this.worldTransform;
      const dx = Math.sqrt(a * a + b * b);
      const dy = Math.sqrt(c * c + d * d);
      const worldScale = (Math.abs(dx) + Math.abs(dy)) / 2;
      const fontScale = this.fontSize / size;
      const resolution = renderer._view.resolution;
      for (const mesh of this._activePagesMeshData) {
        mesh.mesh.shader.uniforms.uFWidth = worldScale * distanceFieldRange * fontScale * resolution;
      }
    }
    super._render(renderer);
  }
  /**
   * Validates text before calling parent's getLocalBounds
   * @returns - The rectangular bounding area
   */
  getLocalBounds() {
    this.validate();
    return super.getLocalBounds();
  }
  /**
   * Updates text when needed
   * @private
   */
  validate() {
    const font = BitmapFont.BitmapFont.available[this._fontName];
    if (!font) {
      throw new Error(`Missing BitmapFont "${this._fontName}"`);
    }
    if (this._font !== font) {
      this.dirty = true;
    }
    if (this.dirty) {
      this.updateText();
    }
  }
  /**
   * The tint of the BitmapText object.
   * @default 0xffffff
   */
  get tint() {
    return this._tintColor.value;
  }
  set tint(value) {
    if (this.tint === value)
      return;
    this._tintColor.setValue(value);
    for (let i = 0; i < this._activePagesMeshData.length; i++) {
      this._activePagesMeshData[i].mesh.tint = value;
    }
  }
  /**
   * The alignment of the BitmapText object.
   * @member {string}
   * @default 'left'
   */
  get align() {
    return this._align;
  }
  set align(value) {
    if (this._align !== value) {
      this._align = value;
      this.dirty = true;
    }
  }
  /** The name of the BitmapFont. */
  get fontName() {
    return this._fontName;
  }
  set fontName(value) {
    if (!BitmapFont.BitmapFont.available[value]) {
      throw new Error(`Missing BitmapFont "${value}"`);
    }
    if (this._fontName !== value) {
      this._fontName = value;
      this.dirty = true;
    }
  }
  /** The size of the font to display. */
  get fontSize() {
    return this._fontSize ?? BitmapFont.BitmapFont.available[this._fontName].size;
  }
  set fontSize(value) {
    if (this._fontSize !== value) {
      this._fontSize = value;
      this.dirty = true;
    }
  }
  /**
   * The anchor sets the origin point of the text.
   *
   * The default is `(0,0)`, this means the text's origin is the top left.
   *
   * Setting the anchor to `(0.5,0.5)` means the text's origin is centered.
   *
   * Setting the anchor to `(1,1)` would mean the text's origin point will be the bottom right corner.
   */
  get anchor() {
    return this._anchor;
  }
  set anchor(value) {
    if (typeof value === "number") {
      this._anchor.set(value);
    } else {
      this._anchor.copyFrom(value);
    }
  }
  /** The text of the BitmapText object. */
  get text() {
    return this._text;
  }
  set text(text) {
    text = String(text === null || text === void 0 ? "" : text);
    if (this._text === text) {
      return;
    }
    this._text = text;
    this.dirty = true;
  }
  /**
   * The max width of this bitmap text in pixels. If the text provided is longer than the
   * value provided, line breaks will be automatically inserted in the last whitespace.
   * Disable by setting the value to 0.
   */
  get maxWidth() {
    return this._maxWidth;
  }
  set maxWidth(value) {
    if (this._maxWidth === value) {
      return;
    }
    this._maxWidth = value;
    this.dirty = true;
  }
  /**
   * The max line height. This is useful when trying to use the total height of the Text,
   * i.e. when trying to vertically align.
   * @readonly
   */
  get maxLineHeight() {
    this.validate();
    return this._maxLineHeight;
  }
  /**
   * The width of the overall text, different from fontSize,
   * which is defined in the style object.
   * @readonly
   */
  get textWidth() {
    this.validate();
    return this._textWidth;
  }
  /** Additional space between characters. */
  get letterSpacing() {
    return this._letterSpacing;
  }
  set letterSpacing(value) {
    if (this._letterSpacing !== value) {
      this._letterSpacing = value;
      this.dirty = true;
    }
  }
  /**
   * If true PixiJS will Math.floor() x/y values when rendering, stopping pixel interpolation.
   * Advantages can include sharper image quality (like text) and faster rendering on canvas.
   * The main disadvantage is movement of objects may appear less smooth.
   * To set the global default, change {@link PIXI.settings.ROUND_PIXELS}
   * @default PIXI.settings.ROUND_PIXELS
   */
  get roundPixels() {
    return this._roundPixels;
  }
  set roundPixels(value) {
    if (value !== this._roundPixels) {
      this._roundPixels = value;
      this.dirty = true;
    }
  }
  /**
   * The height of the overall text, different from fontSize,
   * which is defined in the style object.
   * @readonly
   */
  get textHeight() {
    this.validate();
    return this._textHeight;
  }
  /**
   * The resolution / device pixel ratio of the canvas.
   *
   * This is set to automatically match the renderer resolution by default, but can be overridden by setting manually.
   * @default 1
   */
  get resolution() {
    return this._resolution;
  }
  set resolution(value) {
    this._autoResolution = false;
    if (this._resolution === value) {
      return;
    }
    this._resolution = value;
    this.dirty = true;
  }
  destroy(options) {
    const { _textureCache } = this;
    const data = BitmapFont.BitmapFont.available[this._fontName];
    const pageMeshDataPool = data.distanceFieldType === "none" ? pageMeshDataDefaultPageMeshData : pageMeshDataMSDFPageMeshData;
    pageMeshDataPool.push(...this._activePagesMeshData);
    for (const pageMeshData of this._activePagesMeshData) {
      this.removeChild(pageMeshData.mesh);
    }
    this._activePagesMeshData = [];
    pageMeshDataPool.filter((page) => _textureCache[page.mesh.texture.baseTexture.uid]).forEach((page) => {
      page.mesh.texture = Texture.Texture.EMPTY;
    });
    for (const id in _textureCache) {
      const texture = _textureCache[id];
      texture.destroy();
      delete _textureCache[id];
    }
    this._font = null;
    this._tintColor = null;
    this._textureCache = null;
    super.destroy(options);
  }
}
BitmapText.styleDefaults = {
  align: "left",
  tint: 16777215,
  maxWidth: 0,
  letterSpacing: 0
};

exports.BitmapText = BitmapText;
//# sourceMappingURL=BitmapText.js.map
