'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var Extensions = require('../extensions/Extensions.js');
var Matrix = require('../maths/Matrix.js');
var MeshView = require('../rendering/mesh/shared/MeshView.js');
var ProxyRenderable = require('../rendering/renderers/shared/ProxyRenderable.js');
var QuadGeometry = require('./QuadGeometry.js');
var TilingSpriteShader = require('./shader/TilingSpriteShader.js');

const sharedQuad = new QuadGeometry.QuadGeometry();
class TilingSpritePipe {
  constructor(renderer) {
    this._renderableHash = /* @__PURE__ */ Object.create(null);
    // TODO can prolly merge these properties into a single mesh and
    // add them onto the renderableHash (rather than having them on separate hashes)
    this._gpuBatchedTilingSprite = /* @__PURE__ */ Object.create(null);
    this._gpuTilingSprite = /* @__PURE__ */ Object.create(null);
    this._renderer = renderer;
  }
  validateRenderable(renderable) {
    const textureMatrix = renderable.view.texture.textureMatrix;
    let rebuild = false;
    const renderableData = this._getRenderableData(renderable);
    if (renderableData.batched !== textureMatrix.isSimple) {
      renderableData.batched = textureMatrix.isSimple;
      rebuild = true;
    }
    return rebuild;
  }
  addRenderable(renderable, instructionSet) {
    if (renderable.view._didUpdate) {
      renderable.view._didUpdate = false;
      this._rebuild(renderable);
    }
    const { batched } = this._getRenderableData(renderable);
    if (batched) {
      const batchableTilingSprite = this._getBatchedTilingSprite(renderable);
      this._renderer.renderPipes.mesh.addRenderable(batchableTilingSprite, instructionSet);
    } else {
      const gpuTilingSprite = this._getGpuTilingSprite(renderable);
      this._renderer.renderPipes.mesh.addRenderable(gpuTilingSprite.meshRenderable, instructionSet);
    }
  }
  updateRenderable(renderable) {
    if (renderable.view._didUpdate) {
      renderable.view._didUpdate = false;
      this._rebuild(renderable);
    }
    const { batched } = this._getRenderableData(renderable);
    if (batched) {
      const batchableTilingSprite = this._getBatchedTilingSprite(renderable);
      this._renderer.renderPipes.mesh.updateRenderable(batchableTilingSprite);
    } else {
      const gpuTilingSprite = this._getGpuTilingSprite(renderable);
      this._renderer.renderPipes.mesh.updateRenderable(gpuTilingSprite.meshRenderable);
    }
  }
  destroyRenderable(renderable) {
    this._renderableHash[renderable.uid] = null;
    this._gpuTilingSprite[renderable.uid] = null;
    this._gpuBatchedTilingSprite[renderable.uid] = null;
  }
  _getRenderableData(renderable) {
    return this._renderableHash[renderable.uid] || this._initRenderableData(renderable);
  }
  _initRenderableData(renderable) {
    const renderableData = {
      batched: true
    };
    this._renderableHash[renderable.uid] = renderableData;
    this.validateRenderable(renderable);
    renderable.on("destroyed", () => {
      this.destroyRenderable(renderable);
    });
    return renderableData;
  }
  _rebuild(renderable) {
    const renderableData = this._getRenderableData(renderable);
    const view = renderable.view;
    const textureMatrix = view.texture.textureMatrix;
    if (renderableData.batched) {
      const batchedMesh = this._getBatchedTilingSprite(renderable);
      batchedMesh.view.texture = view.texture;
      view.texture.source.style.addressMode = "repeat";
      view.texture.source.style.update();
      this._updateBatchPositions(renderable);
      this._updateBatchUvs(renderable);
    } else {
      const gpuTilingSprite = this._getGpuTilingSprite(renderable);
      const { meshRenderable } = gpuTilingSprite;
      const meshView = meshRenderable.view;
      meshView.shader.texture = view.texture;
      const tilingUniforms = meshView.shader.resources.tilingUniforms;
      const originalWidth = view.width;
      const originalHeight = view.height;
      const tilingSpriteWidth = view.texture.width;
      const tilingSpriteHeight = view.texture.height;
      const matrix = view._tileTransform.matrix;
      const uTextureTransform = tilingUniforms.uniforms.uTextureTransform;
      uTextureTransform.set(
        matrix.a * tilingSpriteWidth / originalWidth,
        matrix.b * tilingSpriteWidth / originalHeight,
        matrix.c * tilingSpriteHeight / originalWidth,
        matrix.d * tilingSpriteHeight / originalHeight,
        matrix.tx / originalWidth,
        matrix.ty / originalHeight
      );
      uTextureTransform.invert();
      tilingUniforms.uniforms.uMapCoord = textureMatrix.mapCoord;
      tilingUniforms.uniforms.uClampFrame = textureMatrix.uClampFrame;
      tilingUniforms.uniforms.uClampOffset = textureMatrix.uClampOffset;
      tilingUniforms.uniforms.uTextureTransform = uTextureTransform;
      tilingUniforms.uniforms.uSizeAnchor[0] = originalWidth;
      tilingUniforms.uniforms.uSizeAnchor[1] = originalHeight;
      tilingUniforms.uniforms.uSizeAnchor[2] = renderable.view.anchor.x;
      tilingUniforms.uniforms.uSizeAnchor[3] = renderable.view.anchor.y;
      tilingUniforms.update();
    }
  }
  _getGpuTilingSprite(renderable) {
    return this._gpuTilingSprite[renderable.uid] || this._initGpuTilingSprite(renderable);
  }
  _initGpuTilingSprite(renderable) {
    const view = renderable.view;
    const style = view.texture.source.style;
    style.addressMode = "repeat";
    style.update();
    const meshView = new MeshView.MeshView({
      geometry: sharedQuad,
      shader: new TilingSpriteShader.TilingSpriteShader({
        texture: view.texture
      })
    });
    const meshRenderable = new ProxyRenderable.ProxyRenderable({
      original: renderable,
      view: meshView
    });
    const textureMatrix = new Matrix.Matrix();
    const gpuTilingSpriteData = {
      meshRenderable,
      textureMatrix
    };
    this._gpuTilingSprite[renderable.uid] = gpuTilingSpriteData;
    return gpuTilingSpriteData;
  }
  _getBatchedTilingSprite(renderable) {
    return this._gpuBatchedTilingSprite[renderable.uid] || this._initBatchedTilingSprite(renderable);
  }
  _initBatchedTilingSprite(renderable) {
    const meshView = new MeshView.MeshView({
      geometry: new QuadGeometry.QuadGeometry(),
      texture: renderable.view.texture
    });
    const batchableMeshRenderable = new ProxyRenderable.ProxyRenderable({
      original: renderable,
      view: meshView
    });
    this._gpuBatchedTilingSprite[renderable.uid] = batchableMeshRenderable;
    return batchableMeshRenderable;
  }
  _updateBatchPositions(renderable) {
    const meshRenderable = this._getBatchedTilingSprite(renderable);
    const view = renderable.view;
    const positionBuffer = meshRenderable.view.geometry.getBuffer("aPosition");
    const positions = positionBuffer.data;
    const anchorX = view.anchor.x;
    const anchorY = view.anchor.y;
    positions[0] = -anchorX * view.width;
    positions[1] = -anchorY * view.height;
    positions[2] = (1 - anchorX) * view.width;
    positions[3] = -anchorY * view.height;
    positions[4] = (1 - anchorX) * view.width;
    positions[5] = (1 - anchorY) * view.height;
    positions[6] = -anchorX * view.width;
    positions[7] = (1 - anchorY) * view.height;
  }
  _updateBatchUvs(renderable) {
    const view = renderable.view;
    const width = view.texture.frameWidth;
    const height = view.texture.frameHeight;
    const meshRenderable = this._getBatchedTilingSprite(renderable);
    const uvBuffer = meshRenderable.view.geometry.getBuffer("aUV");
    const uvs = uvBuffer.data;
    let anchorX = 0;
    let anchorY = 0;
    if (view._applyAnchorToTexture) {
      anchorX = view.anchor.x;
      anchorY = view.anchor.y;
    }
    uvs[0] = uvs[6] = -anchorX;
    uvs[2] = uvs[4] = 1 - anchorX;
    uvs[1] = uvs[3] = -anchorY;
    uvs[5] = uvs[7] = 1 - anchorY;
    const textureMatrix = Matrix.Matrix.shared;
    textureMatrix.copyFrom(view._tileTransform.matrix);
    textureMatrix.tx /= view.width;
    textureMatrix.ty /= view.height;
    textureMatrix.invert();
    textureMatrix.scale(view.width / width, view.height / height);
    applyMatrix(uvs, 2, 0, textureMatrix);
  }
  destroy() {
    this._renderableHash = null;
    this._gpuTilingSprite = null;
    this._gpuBatchedTilingSprite = null;
    this._renderer = null;
  }
}
/** @ignore */
TilingSpritePipe.extension = {
  type: [
    Extensions.ExtensionType.WebGLPipes,
    Extensions.ExtensionType.WebGPUPipes,
    Extensions.ExtensionType.CanvasPipes
  ],
  name: "tilingSprite"
};
function applyMatrix(array, stride, offset, matrix) {
  let index = 0;
  const size = array.length / (stride || 2);
  const a = matrix.a;
  const b = matrix.b;
  const c = matrix.c;
  const d = matrix.d;
  const tx = matrix.tx;
  const ty = matrix.ty;
  offset *= stride;
  while (index < size) {
    const x = array[offset];
    const y = array[offset + 1];
    array[offset] = a * x + c * y + tx;
    array[offset + 1] = b * x + d * y + ty;
    offset += stride;
    index++;
  }
}

exports.TilingSpritePipe = TilingSpritePipe;
exports.applyMatrix = applyMatrix;
//# sourceMappingURL=TilingSpritePipe.js.map
