import { Point, State, Color, BLEND_MODES, Texture, Polygon, PI_2, Rectangle, RoundedRectangle, Circle, Ellipse, SHAPES, utils, MSAA_QUALITY, DRAW_MODES } from '@pixi/core';
import { Container } from '@pixi/display';
import { curves, LINE_CAP, LINE_JOIN, Graphics, graphicsUtils } from '@pixi/graphics';
import { FillStyle } from './core/FillStyle.mjs';
import { LineStyle, LINE_SCALE_MODE } from './core/LineStyle.mjs';
import { settings } from './settings.mjs';
import { SmoothGraphicsGeometry } from './SmoothGraphicsGeometry.mjs';
import { SmoothGraphicsShader } from './SmoothShader.mjs';

const UnsmoothGraphics = Graphics;
const { BezierUtils, QuadraticUtils, ArcUtils } = graphicsUtils;
const DEFAULT_SHADERS = {};
const _SmoothGraphics = class extends Container {
  constructor(geometry = null) {
    super();
    this._geometry = geometry || new SmoothGraphicsGeometry();
    this._geometry.refCount++;
    this.shader = null;
    this.shaderSettings = {
      maxStyles: settings.SHADER_MAX_STYLES,
      maxTextures: settings.SHADER_MAX_TEXTURES,
      pixelLine: settings.PIXEL_LINE
    };
    this.state = State.for2d();
    this._fillStyle = new FillStyle();
    this._lineStyle = new LineStyle();
    this._matrix = null;
    this._holeMode = false;
    this.currentPath = null;
    this.batches = [];
    this.batchTint = -1;
    this.batchDirty = -1;
    this.vertexData = null;
    this.pluginName = "smooth";
    this._transformID = -1;
    this._tintColor = new Color(16777215);
    this.blendMode = BLEND_MODES.NORMAL;
  }
  get geometry() {
    return this._geometry;
  }
  clone() {
    this.finishPoly();
    return new _SmoothGraphics(this._geometry);
  }
  set blendMode(value) {
    this.state.blendMode = value;
  }
  get blendMode() {
    return this.state.blendMode;
  }
  get tint() {
    return this._tintColor.value;
  }
  set tint(value) {
    this._tintColor.setValue(value);
  }
  get fill() {
    return this._fillStyle;
  }
  get line() {
    return this._lineStyle;
  }
  lineStyle(options = null, color = 0, alpha = 1, alignment = 0.5, scaleMode = settings.LINE_SCALE_MODE) {
    if (typeof options === "number") {
      if (typeof scaleMode === "boolean") {
        scaleMode = scaleMode ? LINE_SCALE_MODE.NONE : LINE_SCALE_MODE.NORMAL;
      }
      options = { width: options, color, alpha, alignment, scaleMode };
    } else {
      const native = options.native;
      if (native !== void 0) {
        options.scaleMode = native ? LINE_SCALE_MODE.NONE : LINE_SCALE_MODE.NORMAL;
      }
    }
    return this.lineTextureStyle(options);
  }
  lineTextureStyle(options) {
    options = Object.assign({
      width: 0,
      texture: Texture.WHITE,
      color: options && options.texture ? 16777215 : 0,
      alpha: 1,
      matrix: null,
      alignment: 0.5,
      native: false,
      cap: LINE_CAP.BUTT,
      join: LINE_JOIN.MITER,
      miterLimit: 10,
      shader: null,
      scaleMode: settings.LINE_SCALE_MODE
    }, options);
    this.normalizeColor(options);
    if (this.currentPath) {
      this.startPoly();
    }
    const visible = options.width > 0 && options.alpha > 0;
    if (!visible) {
      this._lineStyle.reset();
    } else {
      if (options.matrix) {
        options.matrix = options.matrix.clone();
        options.matrix.invert();
      }
      Object.assign(this._lineStyle, { visible }, options);
    }
    return this;
  }
  startPoly() {
    if (this.currentPath) {
      const points = this.currentPath.points;
      const len = this.currentPath.points.length;
      if (len > 2) {
        this.drawShape(this.currentPath);
        this.currentPath = new Polygon();
        this.currentPath.closeStroke = false;
        this.currentPath.points.push(points[len - 2], points[len - 1]);
      }
    } else {
      this.currentPath = new Polygon();
      this.currentPath.closeStroke = false;
    }
  }
  finishPoly() {
    if (this.currentPath) {
      if (this.currentPath.points.length > 2) {
        this.drawShape(this.currentPath);
        this.currentPath = null;
      } else {
        this.currentPath.points.length = 0;
      }
    }
  }
  moveTo(x, y) {
    this.startPoly();
    this.currentPath.points[0] = x;
    this.currentPath.points[1] = y;
    return this;
  }
  lineTo(x, y) {
    if (!this.currentPath) {
      this.moveTo(0, 0);
    }
    const points = this.currentPath.points;
    const fromX = points[points.length - 2];
    const fromY = points[points.length - 1];
    if (fromX !== x || fromY !== y) {
      points.push(x, y);
    }
    return this;
  }
  _initCurve(x = 0, y = 0) {
    if (this.currentPath) {
      if (this.currentPath.points.length === 0) {
        this.currentPath.points = [x, y];
      }
    } else {
      this.moveTo(x, y);
    }
  }
  quadraticCurveTo(cpX, cpY, toX, toY) {
    this._initCurve();
    const points = this.currentPath.points;
    if (points.length === 0) {
      this.moveTo(0, 0);
    }
    QuadraticUtils.curveTo(cpX, cpY, toX, toY, points);
    return this;
  }
  bezierCurveTo(cpX, cpY, cpX2, cpY2, toX, toY) {
    this._initCurve();
    BezierUtils.curveTo(cpX, cpY, cpX2, cpY2, toX, toY, this.currentPath.points);
    return this;
  }
  arcTo(x1, y1, x2, y2, radius) {
    this._initCurve(x1, y1);
    const points = this.currentPath.points;
    const result = ArcUtils.curveTo(x1, y1, x2, y2, radius, points);
    if (result) {
      const { cx, cy, radius: radius2, startAngle, endAngle, anticlockwise } = result;
      this.arc(cx, cy, radius2, startAngle, endAngle, anticlockwise);
    }
    return this;
  }
  arc(cx, cy, radius, startAngle, endAngle, anticlockwise = false) {
    if (startAngle === endAngle) {
      return this;
    }
    if (!anticlockwise && endAngle <= startAngle) {
      endAngle += PI_2;
    } else if (anticlockwise && startAngle <= endAngle) {
      startAngle += PI_2;
    }
    const sweep = endAngle - startAngle;
    if (sweep === 0) {
      return this;
    }
    const startX = cx + Math.cos(startAngle) * radius;
    const startY = cy + Math.sin(startAngle) * radius;
    const eps = this._geometry.closePointEps;
    let points = this.currentPath ? this.currentPath.points : null;
    if (points) {
      const xDiff = Math.abs(points[points.length - 2] - startX);
      const yDiff = Math.abs(points[points.length - 1] - startY);
      if (xDiff < eps && yDiff < eps) ; else {
        points.push(startX, startY);
      }
    } else {
      this.moveTo(startX, startY);
      points = this.currentPath.points;
    }
    ArcUtils.arc(startX, startY, cx, cy, radius, startAngle, endAngle, anticlockwise, points);
    return this;
  }
  beginFill(color = 0, alpha = 1, smooth = false) {
    return this.beginTextureFill({ texture: Texture.WHITE, color, alpha, smooth });
  }
  normalizeColor(options) {
    const temp = Color.shared.setValue(options.color ?? 0);
    options.color = temp.toNumber();
    options.alpha ?? (options.alpha = temp.alpha);
  }
  beginTextureFill(options) {
    options = Object.assign({
      texture: Texture.WHITE,
      color: 16777215,
      alpha: 1,
      matrix: null,
      smooth: false
    }, options);
    this.normalizeColor(options);
    if (this.currentPath) {
      this.startPoly();
    }
    const visible = options.alpha > 0;
    if (!visible) {
      this._fillStyle.reset();
    } else {
      if (options.matrix) {
        options.matrix = options.matrix.clone();
        options.matrix.invert();
      }
      Object.assign(this._fillStyle, { visible }, options);
    }
    return this;
  }
  endFill() {
    this.finishPoly();
    this._fillStyle.reset();
    return this;
  }
  drawRect(x, y, width, height) {
    return this.drawShape(new Rectangle(x, y, width, height));
  }
  drawRoundedRect(x, y, width, height, radius) {
    return this.drawShape(new RoundedRectangle(x, y, width, height, radius));
  }
  drawCircle(x, y, radius) {
    return this.drawShape(new Circle(x, y, radius));
  }
  drawEllipse(x, y, width, height) {
    return this.drawShape(new Ellipse(x, y, width, height));
  }
  drawPolygon(...path) {
    let points;
    let closeStroke = true;
    const poly = path[0];
    if (poly.points) {
      closeStroke = poly.closeStroke;
      points = poly.points;
    } else if (Array.isArray(path[0])) {
      points = path[0];
    } else {
      points = path;
    }
    const shape = new Polygon(points);
    shape.closeStroke = closeStroke;
    this.drawShape(shape);
    return this;
  }
  drawShape(shape) {
    if (!this._holeMode) {
      this._geometry.drawShape(
        shape,
        this._fillStyle.clone(),
        this._lineStyle.clone(),
        this._matrix
      );
    } else {
      this._geometry.drawHole(shape, this._matrix);
    }
    return this;
  }
  clear() {
    this._geometry.clear();
    this._lineStyle.reset();
    this._fillStyle.reset();
    this._boundsID++;
    this._matrix = null;
    this._holeMode = false;
    this.currentPath = null;
    return this;
  }
  isFastRect() {
    const data = this._geometry.graphicsData;
    return data.length === 1 && data[0].shape.type === SHAPES.RECT && !data[0].matrix && !data[0].holes.length && !(data[0].lineStyle.visible && data[0].lineStyle.width);
  }
  _renderCanvas(renderer) {
    UnsmoothGraphics.prototype._renderCanvas.call(this, renderer);
  }
  _render(renderer) {
    this.finishPoly();
    const geometry = this._geometry;
    const hasuint32 = renderer.context.supports.uint32Indices;
    geometry.checkInstancing(renderer.geometry.hasInstance, hasuint32);
    geometry.updateBatches(this.shaderSettings);
    if (geometry.batchable) {
      if (this.batchDirty !== geometry.batchDirty) {
        this._populateBatches();
      }
      this._renderBatched(renderer);
    } else {
      renderer.batch.flush();
      this._renderDirect(renderer);
    }
  }
  _populateBatches() {
    const geometry = this._geometry;
    const blendMode = this.blendMode;
    const len = geometry.batches.length;
    this.batchTint = -1;
    this._transformID = -1;
    this.batchDirty = geometry.batchDirty;
    this.batches.length = len;
    this.vertexData = new Float32Array(geometry.points);
    for (let i = 0; i < len; i++) {
      const gI = geometry.batches[i];
      const color = gI.style.color;
      const vertexData = new Float32Array(
        this.vertexData.buffer,
        gI.attribStart * 4 * 2,
        gI.attribSize * 2
      );
      const batch = {
        vertexData,
        blendMode,
        // indices,
        // uvs,
        _batchRGB: utils.hex2rgb(color),
        _tintRGB: color,
        _texture: gI.style.texture,
        alpha: gI.style.alpha,
        worldAlpha: 1
      };
      this.batches[i] = batch;
    }
  }
  _renderBatched(renderer) {
    if (!this.batches.length) {
      return;
    }
    renderer.batch.setObjectRenderer(renderer.plugins[this.pluginName]);
    this.calculateVertices();
    this.calculateTints();
    for (let i = 0, l = this.batches.length; i < l; i++) {
      const batch = this.batches[i];
      batch.worldAlpha = this.worldAlpha * batch.alpha;
      renderer.plugins[this.pluginName].render(batch);
    }
  }
  _renderDirect(renderer) {
    const directShader = this._resolveDirectShader(renderer);
    let shader = directShader;
    const geometry = this._geometry;
    const worldAlpha = this.worldAlpha;
    const uniforms = shader.uniforms;
    const drawCalls = geometry.drawCalls;
    uniforms.translationMatrix = this.transform.worldTransform;
    Color.shared.setValue(this._tintColor).premultiply(worldAlpha).toArray(uniforms.tint);
    uniforms.resolution = renderer.renderTexture.current ? renderer.renderTexture.current.resolution : renderer.resolution;
    const projTrans = renderer.projection.transform;
    if (projTrans) {
      const scale = Math.sqrt(projTrans.a * projTrans.a + projTrans.b * projTrans.b);
      uniforms.resolution *= scale;
    }
    const multisample = renderer.renderTexture.current ? renderer.renderTexture.current.multisample : renderer.multisample;
    uniforms.expand = (multisample !== MSAA_QUALITY.NONE ? 2 : 1) / uniforms.resolution;
    renderer.shader.bind(shader);
    renderer.geometry.bind(geometry, shader);
    renderer.state.set(this.state);
    shader = null;
    for (let i = 0, l = drawCalls.length; i < l; i++) {
      const drawCall = geometry.drawCalls[i];
      const shaderChange = shader !== drawCall.shader;
      if (shaderChange) {
        shader = drawCall.shader;
        if (shader) {
          shader.uniforms.translationMatrix = this.transform.worldTransform;
          if (shader.uniforms.tint) {
            shader.uniforms.tint[0] = uniforms.tint[0];
            shader.uniforms.tint[1] = uniforms.tint[1];
            shader.uniforms.tint[2] = uniforms.tint[2];
            shader.uniforms.tint[3] = uniforms.tint[3];
          }
        }
      }
      const { texArray, styleArray, size, start } = drawCall;
      const groupTextureCount = texArray.count;
      const shaderHere = shader || directShader;
      const texs = shaderHere.uniforms.styleTextureId;
      const mats = shaderHere.uniforms.styleMatrix;
      const lines = shaderHere.uniforms.styleLine;
      for (let i2 = 0; i2 < styleArray.count; i2++) {
        texs[i2] = styleArray.textureIds[i2];
        lines[i2 * 2] = styleArray.lines[i2 * 2];
        lines[i2 * 2 + 1] = styleArray.lines[i2 * 2 + 1];
        const m = styleArray.matrices[i2];
        mats[i2 * 6] = m.a;
        mats[i2 * 6 + 1] = m.c;
        mats[i2 * 6 + 2] = m.tx;
        mats[i2 * 6 + 3] = m.b;
        mats[i2 * 6 + 4] = m.d;
        mats[i2 * 6 + 5] = m.ty;
      }
      const sizes = shaderHere.uniforms.samplerSize;
      for (let i2 = 0; i2 < groupTextureCount; i2++) {
        sizes[i2 * 2] = texArray.elements[i2].width;
        sizes[i2 * 2 + 1] = texArray.elements[i2].height;
      }
      renderer.shader.bind(shaderHere);
      if (shaderChange) {
        renderer.geometry.bind(geometry);
      }
      for (let j = 0; j < groupTextureCount; j++) {
        renderer.texture.bind(texArray.elements[j], j);
      }
      renderer.geometry.draw(DRAW_MODES.TRIANGLES, size, start);
    }
  }
  _resolveDirectShader(_renderer) {
    let shader = this.shader;
    const pluginName = this.pluginName;
    if (!shader) {
      if (!DEFAULT_SHADERS[pluginName]) {
        DEFAULT_SHADERS[pluginName] = new SmoothGraphicsShader(this.shaderSettings);
      }
      shader = DEFAULT_SHADERS[pluginName];
    }
    return shader;
  }
  _calculateBounds() {
    this.finishPoly();
    const geometry = this._geometry;
    if (!geometry.graphicsData.length) {
      return;
    }
    const { minX, minY, maxX, maxY } = geometry.bounds;
    this._bounds.addFrame(this.transform, minX, minY, maxX, maxY);
  }
  containsPoint(point) {
    this.worldTransform.applyInverse(point, _SmoothGraphics._TEMP_POINT);
    return this._geometry.containsPoint(_SmoothGraphics._TEMP_POINT);
  }
  calculateTints() {
    if (this.batchTint !== this.tint) {
      this.batchTint = this._tintColor.toNumber();
      for (let i = 0; i < this.batches.length; i++) {
        const batch = this.batches[i];
        batch._tintRGB = Color.shared.setValue(this._tintColor).multiply(batch._batchRGB).toLittleEndianNumber();
      }
    }
  }
  calculateVertices() {
    const wtID = this.transform._worldID;
    if (this._transformID === wtID) {
      return;
    }
    this._transformID = wtID;
    const wt = this.transform.worldTransform;
    const a = wt.a;
    const b = wt.b;
    const c = wt.c;
    const d = wt.d;
    const tx = wt.tx;
    const ty = wt.ty;
    const data = this._geometry.points;
    const vertexData = this.vertexData;
    let count = 0;
    for (let i = 0; i < data.length; i += 2) {
      const x = data[i];
      const y = data[i + 1];
      vertexData[count++] = a * x + c * y + tx;
      vertexData[count++] = d * y + b * x + ty;
    }
  }
  closePath() {
    const currentPath = this.currentPath;
    if (currentPath) {
      currentPath.closeStroke = true;
    }
    return this;
  }
  setMatrix(matrix) {
    this._matrix = matrix;
    return this;
  }
  beginHole() {
    this.finishPoly();
    this._holeMode = true;
    return this;
  }
  endHole() {
    this.finishPoly();
    this._holeMode = false;
    return this;
  }
  destroy(options) {
    this._geometry.refCount--;
    if (this._geometry.refCount === 0) {
      this._geometry.dispose();
    }
    this._matrix = null;
    this.currentPath = null;
    this._lineStyle.destroy();
    this._lineStyle = null;
    this._fillStyle.destroy();
    this._fillStyle = null;
    this._geometry = null;
    this.shader = null;
    this.vertexData = null;
    this.batches.length = 0;
    this.batches = null;
    super.destroy(options);
  }
};
let SmoothGraphics = _SmoothGraphics;
SmoothGraphics.curves = curves;
SmoothGraphics._TEMP_POINT = new Point();

export { SmoothGraphics };
//# sourceMappingURL=SmoothGraphics.mjs.map
