'use strict';

require('../core/settings.js');
require('../color.js');
var constants = require('../constants.js');
require('../extensions.js');
require('../math/const.js');
var Rectangle = require('../math/shapes/Rectangle.js');
require('../math/groupD8.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');
require('../utils/media/BoundingBox.js');
var getResolutionOfUrl = require('../utils/network/getResolutionOfUrl.js');
require('../core/autoDetectRenderer.js');
require('../core/background/BackgroundSystem.js');
require('../core/batch/BatchRenderer.js');
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');
var BaseTexture = 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('../text/const.js');
require('../text/Text.js');
var TextMetrics = require('../text/TextMetrics.js');
var TextStyle = require('../text/TextStyle.js');
var BitmapFontData = require('./BitmapFontData.js');
var index = require('./formats/index.js');
var drawGlyph = require('./utils/drawGlyph.js');
var extractCharCode = require('./utils/extractCharCode.js');
var resolveCharacters = require('./utils/resolveCharacters.js');

class BitmapFont {
  /**
   * @param data
   * @param textures
   * @param ownsTextures - Setting to `true` will destroy page textures
   *        when the font is uninstalled.
   */
  constructor(data, textures, ownsTextures) {
    const [info] = data.info;
    const [common] = data.common;
    const [page] = data.page;
    const [distanceField] = data.distanceField;
    const res = getResolutionOfUrl.getResolutionOfUrl(page.file);
    const pageTextures = {};
    this._ownsTextures = ownsTextures;
    this.font = info.face;
    this.size = info.size;
    this.lineHeight = common.lineHeight / res;
    this.chars = {};
    this.pageTextures = pageTextures;
    for (let i = 0; i < data.page.length; i++) {
      const { id, file } = data.page[i];
      pageTextures[id] = textures instanceof Array ? textures[i] : textures[file];
      if (distanceField?.fieldType && distanceField.fieldType !== "none") {
        pageTextures[id].baseTexture.alphaMode = constants.ALPHA_MODES.NO_PREMULTIPLIED_ALPHA;
        pageTextures[id].baseTexture.mipmap = constants.MIPMAP_MODES.OFF;
      }
    }
    for (let i = 0; i < data.char.length; i++) {
      const { id, page: page2 } = data.char[i];
      let { x, y, width, height, xoffset, yoffset, xadvance } = data.char[i];
      x /= res;
      y /= res;
      width /= res;
      height /= res;
      xoffset /= res;
      yoffset /= res;
      xadvance /= res;
      const rect = new Rectangle.Rectangle(x + pageTextures[page2].frame.x / res, y + pageTextures[page2].frame.y / res, width, height);
      this.chars[id] = {
        xOffset: xoffset,
        yOffset: yoffset,
        xAdvance: xadvance,
        kerning: {},
        texture: new Texture.Texture(pageTextures[page2].baseTexture, rect),
        page: page2
      };
    }
    for (let i = 0; i < data.kerning.length; i++) {
      let { first, second, amount } = data.kerning[i];
      first /= res;
      second /= res;
      amount /= res;
      if (this.chars[second]) {
        this.chars[second].kerning[first] = amount;
      }
    }
    this.distanceFieldRange = distanceField?.distanceRange;
    this.distanceFieldType = distanceField?.fieldType?.toLowerCase() ?? "none";
  }
  /** Remove references to created glyph textures. */
  destroy() {
    for (const id in this.chars) {
      this.chars[id].texture.destroy();
      this.chars[id].texture = null;
    }
    for (const id in this.pageTextures) {
      if (this._ownsTextures) {
        this.pageTextures[id].destroy(true);
      }
      this.pageTextures[id] = null;
    }
    this.chars = null;
    this.pageTextures = null;
  }
  /**
   * Register a new bitmap font.
   * @param data - The
   *        characters map that could be provided as xml or raw string.
   * @param textures - List of textures for each page.
   * @param ownsTextures - Set to `true` to destroy page textures
   *        when the font is uninstalled. By default fonts created with
   *        `BitmapFont.from` or from the `BitmapFontLoader` are `true`.
   * @returns {PIXI.BitmapFont} Result font object with font, size, lineHeight
   *         and char fields.
   */
  static install(data, textures, ownsTextures) {
    let fontData;
    if (data instanceof BitmapFontData.BitmapFontData) {
      fontData = data;
    } else {
      const format = index.autoDetectFormat(data);
      if (!format) {
        throw new Error("Unrecognized data format for font.");
      }
      fontData = format.parse(data);
    }
    if (textures instanceof Texture.Texture) {
      textures = [textures];
    }
    const font = new BitmapFont(fontData, textures, ownsTextures);
    BitmapFont.available[font.font] = font;
    return font;
  }
  /**
   * Remove bitmap font by name.
   * @param name - Name of the font to uninstall.
   */
  static uninstall(name) {
    const font = BitmapFont.available[name];
    if (!font) {
      throw new Error(`No font found named '${name}'`);
    }
    font.destroy();
    delete BitmapFont.available[name];
  }
  /**
   * Generates a bitmap-font for the given style and character set. This does not support
   * kernings yet. With `style` properties, only the following non-layout properties are used:
   *
   * - {@link PIXI.TextStyle#dropShadow|dropShadow}
   * - {@link PIXI.TextStyle#dropShadowDistance|dropShadowDistance}
   * - {@link PIXI.TextStyle#dropShadowColor|dropShadowColor}
   * - {@link PIXI.TextStyle#dropShadowBlur|dropShadowBlur}
   * - {@link PIXI.TextStyle#dropShadowAngle|dropShadowAngle}
   * - {@link PIXI.TextStyle#fill|fill}
   * - {@link PIXI.TextStyle#fillGradientStops|fillGradientStops}
   * - {@link PIXI.TextStyle#fillGradientType|fillGradientType}
   * - {@link PIXI.TextStyle#fontFamily|fontFamily}
   * - {@link PIXI.TextStyle#fontSize|fontSize}
   * - {@link PIXI.TextStyle#fontVariant|fontVariant}
   * - {@link PIXI.TextStyle#fontWeight|fontWeight}
   * - {@link PIXI.TextStyle#lineJoin|lineJoin}
   * - {@link PIXI.TextStyle#miterLimit|miterLimit}
   * - {@link PIXI.TextStyle#stroke|stroke}
   * - {@link PIXI.TextStyle#strokeThickness|strokeThickness}
   * - {@link PIXI.TextStyle#textBaseline|textBaseline}
   * @param name - The name of the custom font to use with BitmapText.
   * @param textStyle - Style options to render with BitmapFont.
   * @param options - Setup options for font or name of the font.
   * @returns Font generated by style options.
   * @example
   * import { BitmapFont, BitmapText } from 'pixi.js';
   *
   * BitmapFont.from('TitleFont', {
   *     fontFamily: 'Arial',
   *     fontSize: 12,
   *     strokeThickness: 2,
   *     fill: 'purple',
   * });
   *
   * const title = new BitmapText('This is the title', { fontName: 'TitleFont' });
   */
  static from(name, textStyle, options) {
    if (!name) {
      throw new Error("[BitmapFont] Property `name` is required.");
    }
    const { chars, padding, resolution, textureWidth, textureHeight, ...baseOptions } = Object.assign({}, BitmapFont.defaultOptions, options);
    const charsList = resolveCharacters.resolveCharacters(chars);
    const style = textStyle instanceof TextStyle.TextStyle ? textStyle : new TextStyle.TextStyle(textStyle);
    const lineWidth = textureWidth;
    const fontData = new BitmapFontData.BitmapFontData();
    fontData.info[0] = {
      face: style.fontFamily,
      size: style.fontSize
    };
    fontData.common[0] = {
      lineHeight: style.fontSize
    };
    let positionX = 0;
    let positionY = 0;
    let canvas;
    let context;
    let baseTexture;
    let maxCharHeight = 0;
    const textures = [];
    for (let i = 0; i < charsList.length; i++) {
      if (!canvas) {
        canvas = settings.settings.ADAPTER.createCanvas();
        canvas.width = textureWidth;
        canvas.height = textureHeight;
        context = canvas.getContext("2d");
        baseTexture = new BaseTexture.BaseTexture(canvas, { resolution, ...baseOptions });
        textures.push(new Texture.Texture(baseTexture));
        fontData.page.push({
          id: textures.length - 1,
          file: ""
        });
      }
      const character = charsList[i];
      const metrics = TextMetrics.TextMetrics.measureText(character, style, false, canvas);
      const width = metrics.width;
      const height = Math.ceil(metrics.height);
      const textureGlyphWidth = Math.ceil((style.fontStyle === "italic" ? 2 : 1) * width);
      if (positionY >= textureHeight - height * resolution) {
        if (positionY === 0) {
          throw new Error(`[BitmapFont] textureHeight ${textureHeight}px is too small (fontFamily: '${style.fontFamily}', fontSize: ${style.fontSize}px, char: '${character}')`);
        }
        --i;
        canvas = null;
        context = null;
        baseTexture = null;
        positionY = 0;
        positionX = 0;
        maxCharHeight = 0;
        continue;
      }
      maxCharHeight = Math.max(height + metrics.fontProperties.descent, maxCharHeight);
      if (textureGlyphWidth * resolution + positionX >= lineWidth) {
        if (positionX === 0) {
          throw new Error(`[BitmapFont] textureWidth ${textureWidth}px is too small (fontFamily: '${style.fontFamily}', fontSize: ${style.fontSize}px, char: '${character}')`);
        }
        --i;
        positionY += maxCharHeight * resolution;
        positionY = Math.ceil(positionY);
        positionX = 0;
        maxCharHeight = 0;
        continue;
      }
      drawGlyph.drawGlyph(canvas, context, metrics, positionX, positionY, resolution, style);
      const id = extractCharCode.extractCharCode(metrics.text);
      fontData.char.push({
        id,
        page: textures.length - 1,
        x: positionX / resolution,
        y: positionY / resolution,
        width: textureGlyphWidth,
        height,
        xoffset: 0,
        yoffset: 0,
        xadvance: width - (style.dropShadow ? style.dropShadowDistance : 0) - (style.stroke ? style.strokeThickness : 0)
      });
      positionX += (textureGlyphWidth + 2 * padding) * resolution;
      positionX = Math.ceil(positionX);
    }
    for (let i = 0, len = charsList.length; i < len; i++) {
      const first = charsList[i];
      for (let j = 0; j < len; j++) {
        const second = charsList[j];
        const c1 = context.measureText(first).width;
        const c2 = context.measureText(second).width;
        const total = context.measureText(first + second).width;
        const amount = total - (c1 + c2);
        if (amount) {
          fontData.kerning.push({
            first: extractCharCode.extractCharCode(first),
            second: extractCharCode.extractCharCode(second),
            amount
          });
        }
      }
    }
    const font = new BitmapFont(fontData, textures, true);
    if (BitmapFont.available[name] !== void 0) {
      BitmapFont.uninstall(name);
    }
    BitmapFont.available[name] = font;
    return font;
  }
}
BitmapFont.ALPHA = [["a", "z"], ["A", "Z"], " "];
BitmapFont.NUMERIC = [["0", "9"]];
BitmapFont.ALPHANUMERIC = [["a", "z"], ["A", "Z"], ["0", "9"], " "];
BitmapFont.ASCII = [[" ", "~"]];
BitmapFont.defaultOptions = {
  resolution: 1,
  textureWidth: 512,
  textureHeight: 512,
  padding: 4,
  chars: BitmapFont.ALPHANUMERIC
};
BitmapFont.available = {};

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