import { ExtensionType } from '../../../extensions/Extensions.mjs';
import { Matrix } from '../../../maths/Matrix.mjs';
import { isRenderingToScreen } from '../shared/renderTarget/isRenderingToScreen.mjs';
import { RenderTarget } from '../shared/renderTarget/RenderTarget.mjs';
import { SystemRunner } from '../shared/system/SystemRunner.mjs';
import { Texture } from '../shared/texture/Texture.mjs';
import { getCanvasTexture } from '../shared/texture/utils/getCanvasTexture.mjs';
import { CLEAR } from './const.mjs';
import { GlRenderTarget } from './GlRenderTarget.mjs';

class GlRenderTargetSystem {
  constructor(renderer) {
    this.onRenderTargetChange = new SystemRunner("onRenderTargetChange");
    this._renderSurfaceToRenderTargetHash = /* @__PURE__ */ new Map();
    this._gpuRenderTargetHash = /* @__PURE__ */ Object.create(null);
    this._renderTargetStack = [];
    this._defaultClearColor = [0, 0, 0, 0];
    this._clearColorCache = [0, 0, 0, 0];
    this._viewPortCache = {
      x: 0,
      y: 0,
      width: 0,
      height: 0
    };
    this.rootProjectionMatrix = new Matrix();
    this._renderer = renderer;
  }
  contextChange(gl) {
    this._gl = gl;
  }
  start(rootRenderSurface, clear = true, clearColor) {
    this._renderTargetStack.length = 0;
    const renderTarget = this.getRenderTarget(rootRenderSurface);
    this.rootRenderTarget = renderTarget;
    this.renderingToScreen = isRenderingToScreen(this.rootRenderTarget);
    this.rootProjectionMatrix = renderTarget.projectionMatrix;
    this.push(renderTarget, clear, clearColor);
  }
  bind(renderSurface, clear = true, clearColor) {
    const renderTarget = this.getRenderTarget(renderSurface);
    this.renderTarget = renderTarget;
    const gpuRenderTarget = this.getGpuRenderTarget(renderTarget);
    if (renderTarget.dirtyId !== gpuRenderTarget.dirtyId) {
      gpuRenderTarget.dirtyId = renderTarget.dirtyId;
      this._resizeGpuRenderTarget(renderTarget);
    }
    const gl = this._gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, gpuRenderTarget.framebuffer);
    renderTarget.colorTextures.forEach((texture) => {
      this._renderer.texture.unbind(texture);
    });
    const viewport = renderTarget.viewport;
    let viewPortY = viewport.y;
    if (renderTarget.isRoot) {
      viewPortY = this._renderer.view.element.height - viewport.height;
    }
    const viewPortCache = this._viewPortCache;
    if (viewPortCache.x !== viewport.x || viewPortCache.y !== viewPortY || viewPortCache.width !== viewport.width || viewPortCache.height !== viewport.height) {
      viewPortCache.x = viewport.x;
      viewPortCache.y = viewPortY;
      viewPortCache.width = viewport.width;
      viewPortCache.height = viewport.height;
      gl.viewport(
        viewport.x,
        viewPortY,
        viewport.width,
        viewport.height
      );
    }
    this.clear(clear, clearColor);
    this.onRenderTargetChange.emit(renderTarget);
    return renderTarget;
  }
  clear(clear, clearColor) {
    if (!clear)
      return;
    if (typeof clear === "boolean") {
      clear = clear ? CLEAR.ALL : CLEAR.NONE;
    }
    const gl = this._gl;
    if (clear & CLEAR.COLOR) {
      clearColor ?? (clearColor = this._defaultClearColor);
      const clearColorCache = this._clearColorCache;
      const clearColorArray = clearColor;
      if (clearColorCache[0] !== clearColorArray[0] || clearColorCache[1] !== clearColorArray[1] || clearColorCache[2] !== clearColorArray[2] || clearColorCache[3] !== clearColorArray[3]) {
        clearColorCache[0] = clearColorArray[0];
        clearColorCache[1] = clearColorArray[1];
        clearColorCache[2] = clearColorArray[2];
        clearColorCache[3] = clearColorArray[3];
        gl.clearColor(clearColorArray[0], clearColorArray[1], clearColorArray[2], clearColorArray[3]);
      }
    }
    gl.clear(clear);
  }
  push(renderSurface, clear = true, clearColor) {
    const renderTarget = this.bind(renderSurface, clear, clearColor);
    this._renderTargetStack.push(renderTarget);
    return renderTarget;
  }
  pop() {
    this._renderTargetStack.pop();
    this.bind(this._renderTargetStack[this._renderTargetStack.length - 1], false);
  }
  getRenderTarget(renderSurface) {
    return this._renderSurfaceToRenderTargetHash.get(renderSurface) ?? this._initRenderTarget(renderSurface);
  }
  _initRenderTarget(renderSurface) {
    let renderTarget = null;
    if (renderSurface instanceof HTMLCanvasElement) {
      renderSurface = getCanvasTexture(renderSurface);
    }
    if (renderSurface instanceof RenderTarget) {
      renderTarget = renderSurface;
    } else if (renderSurface instanceof Texture) {
      renderTarget = new RenderTarget({
        colorTextures: [renderSurface]
      });
      if (renderSurface.source.resource instanceof HTMLCanvasElement) {
        renderTarget.isRoot = true;
      }
      renderSurface.source.on("destroy", () => {
        renderTarget.destroy();
      });
    }
    this._renderSurfaceToRenderTargetHash.set(renderSurface, renderTarget);
    return renderTarget;
  }
  finishRenderPass(renderTarget) {
    renderTarget = renderTarget || this.renderTarget;
    const glRenderTarget = this.getGpuRenderTarget(renderTarget);
    if (!glRenderTarget.msaa)
      return;
    const gl = this._renderer.gl;
    gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.resolveTargetFramebuffer);
    gl.bindFramebuffer(gl.READ_FRAMEBUFFER, glRenderTarget.framebuffer);
    gl.blitFramebuffer(
      0,
      0,
      glRenderTarget.width,
      glRenderTarget.height,
      0,
      0,
      glRenderTarget.width,
      glRenderTarget.height,
      gl.COLOR_BUFFER_BIT,
      gl.NEAREST
    );
    gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.framebuffer);
  }
  copyToTexture(sourceRenderSurfaceTexture, destinationTexture, origin, size) {
    const renderer = this._renderer;
    const glRenderTarget = this.getGpuRenderTarget(sourceRenderSurfaceTexture);
    const gl = renderer.gl;
    this.finishRenderPass(sourceRenderSurfaceTexture);
    gl.bindFramebuffer(gl.FRAMEBUFFER, glRenderTarget.resolveTargetFramebuffer);
    renderer.texture.bind(destinationTexture, 0);
    gl.copyTexSubImage2D(
      gl.TEXTURE_2D,
      0,
      0,
      0,
      origin.x,
      origin.y,
      size.width,
      size.height
    );
    return destinationTexture;
  }
  getGpuRenderTarget(renderTarget) {
    return this._gpuRenderTargetHash[renderTarget.uid] || this._initGpuRenderTarget(renderTarget);
  }
  _initGpuRenderTarget(renderTarget) {
    const renderer = this._renderer;
    const gl = renderer.gl;
    const glRenderTarget = new GlRenderTarget();
    if (renderTarget.colorTexture.source.resource instanceof HTMLCanvasElement) {
      this._gpuRenderTargetHash[renderTarget.uid] = glRenderTarget;
      glRenderTarget.framebuffer = null;
      return glRenderTarget;
    }
    this._initColor(renderTarget, glRenderTarget);
    if (renderTarget.stencil) {
      this._initStencil(glRenderTarget);
    }
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    this._gpuRenderTargetHash[renderTarget.uid] = glRenderTarget;
    return glRenderTarget;
  }
  _resizeGpuRenderTarget(renderTarget) {
    if (renderTarget.isRoot)
      return;
    const glRenderTarget = this.getGpuRenderTarget(renderTarget);
    this._resizeColor(renderTarget, glRenderTarget);
    if (renderTarget.stencil) {
      this._resizeStencil(glRenderTarget);
    }
  }
  _initColor(renderTarget, glRenderTarget) {
    const renderer = this._renderer;
    const gl = renderer.gl;
    const resolveTargetFramebuffer = gl.createFramebuffer();
    glRenderTarget.resolveTargetFramebuffer = resolveTargetFramebuffer;
    gl.bindFramebuffer(gl.FRAMEBUFFER, resolveTargetFramebuffer);
    glRenderTarget.width = renderTarget.colorTexture.source.pixelWidth;
    glRenderTarget.height = renderTarget.colorTexture.source.pixelHeight;
    renderTarget.colorTextures.forEach((colorTexture, i) => {
      const source = colorTexture.source;
      if (source.antialias) {
        glRenderTarget.msaa = true;
      }
      renderer.texture.bindSource(source, 0);
      const glSource = renderer.texture.getGlSource(source);
      const glTexture = glSource.texture;
      gl.framebufferTexture2D(
        gl.FRAMEBUFFER,
        gl.COLOR_ATTACHMENT0 + i,
        3553,
        // texture.target,
        glTexture,
        0
      );
    });
    if (glRenderTarget.msaa) {
      const viewFramebuffer = gl.createFramebuffer();
      glRenderTarget.framebuffer = viewFramebuffer;
      gl.bindFramebuffer(gl.FRAMEBUFFER, viewFramebuffer);
      renderTarget.colorTextures.forEach((_, i) => {
        const msaaRenderBuffer = gl.createRenderbuffer();
        glRenderTarget.msaaRenderBuffer[i] = msaaRenderBuffer;
      });
    } else {
      glRenderTarget.framebuffer = resolveTargetFramebuffer;
    }
  }
  _resizeColor(renderTarget, glRenderTarget) {
    const source = renderTarget.colorTexture.source;
    glRenderTarget.width = source.pixelWidth;
    glRenderTarget.height = source.pixelHeight;
    renderTarget.colorTextures.forEach((colorTexture, i) => {
      if (i === 0)
        return;
      colorTexture.source.resize(source.width, source.height, source._resolution);
    });
    if (glRenderTarget.msaa) {
      const renderer = this._renderer;
      const gl = renderer.gl;
      const viewFramebuffer = glRenderTarget.framebuffer;
      gl.bindFramebuffer(gl.FRAMEBUFFER, viewFramebuffer);
      renderTarget.colorTextures.forEach((colorTexture, i) => {
        const source2 = colorTexture.source;
        renderer.texture.bindSource(source2, 0);
        const glSource = renderer.texture.getGlSource(source2);
        const glInternalFormat = glSource.internalFormat;
        const msaaRenderBuffer = glRenderTarget.msaaRenderBuffer[i];
        gl.bindRenderbuffer(
          gl.RENDERBUFFER,
          msaaRenderBuffer
        );
        gl.renderbufferStorageMultisample(
          gl.RENDERBUFFER,
          4,
          glInternalFormat,
          source2.pixelWidth,
          source2.pixelHeight
        );
        gl.framebufferRenderbuffer(
          gl.FRAMEBUFFER,
          gl.COLOR_ATTACHMENT0 + i,
          gl.RENDERBUFFER,
          msaaRenderBuffer
        );
      });
    }
  }
  _initStencil(glRenderTarget) {
    const gl = this._renderer.gl;
    const depthStencilRenderBuffer = gl.createRenderbuffer();
    glRenderTarget.depthStencilRenderBuffer = depthStencilRenderBuffer;
    gl.bindRenderbuffer(
      gl.RENDERBUFFER,
      depthStencilRenderBuffer
    );
    gl.framebufferRenderbuffer(
      gl.FRAMEBUFFER,
      gl.DEPTH_STENCIL_ATTACHMENT,
      gl.RENDERBUFFER,
      depthStencilRenderBuffer
    );
  }
  _resizeStencil(glRenderTarget) {
    const gl = this._renderer.gl;
    gl.bindRenderbuffer(
      gl.RENDERBUFFER,
      glRenderTarget.depthStencilRenderBuffer
    );
    if (glRenderTarget.msaa) {
      gl.renderbufferStorageMultisample(
        gl.RENDERBUFFER,
        4,
        gl.DEPTH24_STENCIL8,
        glRenderTarget.width,
        glRenderTarget.height
      );
    } else {
      gl.renderbufferStorage(
        gl.RENDERBUFFER,
        gl.DEPTH24_STENCIL8,
        glRenderTarget.width,
        glRenderTarget.height
      );
    }
  }
}
/** @ignore */
GlRenderTargetSystem.extension = {
  type: [
    ExtensionType.WebGLSystem
  ],
  name: "renderTarget"
};

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