import { m as macro } from '../../macros2.js';
import vtkOpenGLTexture from './Texture.js';
import vtkOpenGLFramebuffer from './Framebuffer.js';
import vtkRenderPass from '../SceneGraph/RenderPass.js';
import vtkDataArray from '../../Common/Core/DataArray.js';
import vtkHelper from './Helper.js';
import vtkProperty from '../Core/Property.js';
import vtkShaderProgram from './ShaderProgram.js';
import vtkVertexArrayObject from './VertexArrayObject.js';

const {
  Representation
} = vtkProperty;
const {
  vtkErrorMacro
} = macro;

// ----------------------------------------------------------------------------

function translucentShaderReplacement(shaders) {
  const substituteRes = vtkShaderProgram.substitute(shaders.Fragment, '//VTK::RenderPassFragmentShader::Impl', `
      float weight = gl_FragData[0].a * pow(max(1.1 - gl_FragCoord.z, 0.0), 2.0);
      gl_FragData[0] = vec4(gl_FragData[0].rgb*weight, gl_FragData[0].a);
      gl_FragData[1].r = weight;
    `, false);
  shaders.Fragment = substituteRes.result;
}
const oitpFragTemplate = `//VTK::System::Dec

in vec2 tcoord;

uniform sampler2D translucentRTexture;
uniform sampler2D translucentRGBATexture;

// the output of this shader
//VTK::Output::Dec

void main()
{
  vec4 t1Color = texture(translucentRGBATexture, tcoord);
  float t2Color = texture(translucentRTexture, tcoord).r;
  gl_FragData[0] = vec4(t1Color.rgb/max(t2Color,0.01), 1.0 - t1Color.a);
}
`;
function vtkOpenGLOrderIndependentTranslucentPass(publicAPI, model) {
  // Set our className
  model.classHierarchy.push('vtkOpenGLOrderIndependentTranslucentPass');

  // build vertices etc
  publicAPI.createVertexBuffer = () => {
    // 4 corner points in clipping space in order (x, y, z) where z is always set to -1
    // prettier-ignore
    const ptsArray = new Float32Array([-1, -1, -1, 1, -1, -1, -1, 1, -1, 1, 1, -1]);

    // 4 corresponding corner points in texture space in order (x, y)
    const tcoordArray = new Float32Array([0, 0, 1, 0, 0, 1, 1, 1]);

    // a square defined as cell relation ship in order (cell_size, v1, v2, v3, v4)
    const cellArray = new Uint16Array([4, 0, 1, 3, 2]);
    const points = vtkDataArray.newInstance({
      numberOfComponents: 3,
      values: ptsArray
    });
    points.setName('points');
    const tcoords = vtkDataArray.newInstance({
      numberOfComponents: 2,
      values: tcoordArray
    });
    tcoords.setName('tcoords');
    const cells = vtkDataArray.newInstance({
      numberOfComponents: 1,
      values: cellArray
    });
    model.tris.getCABO().createVBO(cells, 'polys', Representation.SURFACE, {
      points,
      tcoords,
      cellOffset: 0
    });
    model.VBOBuildTime.modified();
  };
  publicAPI.createFramebuffer = viewNode => {
    const size = viewNode.getSize();
    const gl = viewNode.getContext();
    model.framebuffer = vtkOpenGLFramebuffer.newInstance();
    model.framebuffer.setOpenGLRenderWindow(viewNode);
    model.framebuffer.create(...size);
    model.framebuffer.saveCurrentBindingsAndBuffers();
    model.framebuffer.bind();
    model.translucentRGBATexture = vtkOpenGLTexture.newInstance();
    model.translucentRGBATexture.setInternalFormat(gl.RGBA16F);
    model.translucentRGBATexture.setFormat(gl.RGBA);
    model.translucentRGBATexture.setOpenGLDataType(gl.HALF_FLOAT);
    model.translucentRGBATexture.setOpenGLRenderWindow(viewNode);
    model.translucentRGBATexture.create2DFromRaw(size[0], size[1], 4, 'Float32Array', null);
    model.translucentRTexture = vtkOpenGLTexture.newInstance();
    model.translucentRTexture.setInternalFormat(gl.R16F);
    model.translucentRTexture.setFormat(gl.RED);
    model.translucentRTexture.setOpenGLDataType(gl.HALF_FLOAT);
    model.translucentRTexture.setOpenGLRenderWindow(viewNode);
    model.translucentRTexture.create2DFromRaw(size[0], size[1], 1, 'Float32Array', null);
    model.translucentZTexture = vtkOpenGLTexture.newInstance();
    model.translucentZTexture.setOpenGLRenderWindow(viewNode);
    model.translucentZTexture.createDepthFromRaw(size[0], size[1], 'Float32Array', null);
    model.framebuffer.setColorBuffer(model.translucentRGBATexture, 0);
    model.framebuffer.setColorBuffer(model.translucentRTexture, 1);
    model.framebuffer.setDepthBuffer(model.translucentZTexture);
  };
  publicAPI.createCopyShader = viewNode => {
    model.copyShader = viewNode.getShaderCache().readyShaderProgramArray(['//VTK::System::Dec', 'attribute vec4 vertexDC;', 'attribute vec2 tcoordTC;', 'varying vec2 tcoord;', 'void main() { tcoord = tcoordTC; gl_Position = vertexDC; }'].join('\n'), oitpFragTemplate, '');
  };
  publicAPI.createVBO = viewNode => {
    const gl = viewNode.getContext();
    model.tris.setOpenGLRenderWindow(viewNode);
    publicAPI.createVertexBuffer();
    const program = model.copyShader;
    // prepare the vertex and triangle data for the image plane to render to

    model.tris.getCABO().bind();
    if (!model.copyVAO.addAttributeArray(program, model.tris.getCABO(), 'vertexDC', model.tris.getCABO().getVertexOffset(), model.tris.getCABO().getStride(), gl.FLOAT, 3, gl.FALSE)) {
      vtkErrorMacro('Error setting vertexDC in copy shader VAO.');
    }
    if (!model.copyVAO.addAttributeArray(program, model.tris.getCABO(), 'tcoordTC', model.tris.getCABO().getTCoordOffset(), model.tris.getCABO().getStride(), gl.FLOAT, 2, gl.FALSE)) {
      vtkErrorMacro('Error setting vertexDC in copy shader VAO.');
    }
  };
  publicAPI.traverse = (viewNode, renNode, forwardPass) => {
    if (model.deleted) {
      return;
    }
    const size = viewNode.getSize();
    const gl = viewNode.getContext();

    // if we lack the webgl2 and half floatsupport just do
    // basic alpha blending
    model._supported = false;
    if (renNode.getSelector() || !gl || !viewNode.getWebgl2() || !gl.getExtension('EXT_color_buffer_half_float') && !gl.getExtension('EXT_color_buffer_float')) {
      publicAPI.setCurrentOperation('translucentPass');
      renNode.traverse(publicAPI);
      return;
    }
    model._supported = true;

    // prepare framebuffer // allocate framebuffer if needed and bind it
    if (model.framebuffer === null) {
      publicAPI.createFramebuffer(viewNode);
    } else {
      const fbSize = model.framebuffer.getSize();
      if (fbSize === null || fbSize[0] !== size[0] || fbSize[1] !== size[1]) {
        model.framebuffer.releaseGraphicsResources();
        model.translucentRGBATexture.releaseGraphicsResources(viewNode);
        model.translucentRTexture.releaseGraphicsResources(viewNode);
        model.translucentZTexture.releaseGraphicsResources(viewNode);
        publicAPI.createFramebuffer(viewNode);
      } else {
        // store framebuffer bindings to restore them later
        model.framebuffer.saveCurrentBindingsAndBuffers();
        model.framebuffer.bind();
      }
    }
    gl.drawBuffers([gl.COLOR_ATTACHMENT0]);
    gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 0.0]);
    gl.clearBufferfv(gl.DEPTH, 0, [1.0]);
    gl.colorMask(false, false, false, false);

    // rerender the opaque pass to set the depth buffer
    // TODO remove when webgl1 is deprecated and instead
    // have the forward pass use a texture backed zbuffer
    if (forwardPass.getOpaqueActorCount() > 0) {
      // Don't use zBufferPass as it will also render the depth of translucent actors
      forwardPass.setCurrentOperation('opaqueZBufferPass');
      renNode.traverse(forwardPass);
    }
    gl.colorMask(true, true, true, true);
    gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);

    // make sure to clear the entire framebuffer as we will
    // be blitting the entire thing all of it needs good initial values
    gl.viewport(0, 0, size[0], size[1]);
    gl.scissor(0, 0, size[0], size[1]);
    gl.clearBufferfv(gl.COLOR, 0, [0.0, 0.0, 0.0, 1.0]);
    gl.clearBufferfv(gl.COLOR, 1, [0.0, 0.0, 0.0, 0.0]);
    gl.enable(gl.DEPTH_TEST);
    gl.enable(gl.BLEND);

    // basic gist is we accumulate color into RGB We compute final opacity
    // into A We store accumulated opacity into R of the R texture.
    gl.blendFuncSeparate(gl.ONE, gl.ONE, gl.ZERO, gl.ONE_MINUS_SRC_ALPHA);

    // now do the translucent rendering
    publicAPI.setCurrentOperation('translucentPass');
    renNode.traverse(publicAPI);
    gl.drawBuffers([gl.NONE]);
    model.framebuffer.restorePreviousBindingsAndBuffers();
    // gl.drawBuffers([gl.BACK]);

    // make sure the copy shader is ready
    if (model.copyShader === null) {
      publicAPI.createCopyShader(viewNode);
    } else {
      viewNode.getShaderCache().readyShaderProgram(model.copyShader);
    }

    // make sure we have a VAO
    if (!model.copyVAO) {
      model.copyVAO = vtkVertexArrayObject.newInstance();
      model.copyVAO.setOpenGLRenderWindow(viewNode);
    }
    model.copyVAO.bind();

    // make sure the VBO is up to date
    if (model.VBOBuildTime.getMTime() < publicAPI.getMTime()) {
      publicAPI.createVBO(viewNode);
    }
    gl.blendFuncSeparate(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA, gl.ONE, gl.ONE_MINUS_SRC_ALPHA);
    gl.depthMask(false);
    gl.depthFunc(gl.ALWAYS);
    gl.viewport(0, 0, size[0], size[1]);
    gl.scissor(0, 0, size[0], size[1]);

    // activate texture
    model.translucentRGBATexture.activate();
    model.copyShader.setUniformi('translucentRGBATexture', model.translucentRGBATexture.getTextureUnit());
    model.translucentRTexture.activate();
    model.copyShader.setUniformi('translucentRTexture', model.translucentRTexture.getTextureUnit());

    // render quad
    gl.drawArrays(gl.TRIANGLES, 0, model.tris.getCABO().getElementCount());
    gl.depthMask(true);
    gl.depthFunc(gl.LEQUAL);
    model.translucentRGBATexture.deactivate();
    model.translucentRTexture.deactivate();

    // restore scissor + viewport from renderer
    const ts = renNode.getTiledSizeAndOrigin();
    gl.scissor(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize);
    gl.viewport(ts.lowerLeftU, ts.lowerLeftV, ts.usize, ts.vsize);
  };
  publicAPI.getShaderReplacement = () => {
    if (model._supported) {
      return translucentShaderReplacement;
    }
    return null;
  };
  publicAPI.releaseGraphicsResources = viewNode => {
    if (model.framebuffer) {
      model.framebuffer.releaseGraphicsResources(viewNode);
      model.framebuffer = null;
    }
    if (model.translucentRGBATexture) {
      model.translucentRGBATexture.releaseGraphicsResources(viewNode);
      model.translucentRGBATexture = null;
    }
    if (model.translucentRTexture) {
      model.translucentRTexture.releaseGraphicsResources(viewNode);
      model.translucentRTexture = null;
    }
    if (model.translucentZTexture) {
      model.translucentZTexture.releaseGraphicsResources(viewNode);
      model.translucentZTexture = null;
    }
    if (model.copyVAO) {
      model.copyVAO.releaseGraphicsResources(viewNode);
      model.copyVAO = null;
    }
    if (model.copyShader) {
      model.copyShader.releaseGraphicsResources(viewNode);
      model.copyShader = null;
    }
    if (model.tris) {
      model.tris.releaseGraphicsResources(viewNode);
      model.tris = null;
    }
    publicAPI.modified();
  };
}

// ----------------------------------------------------------------------------
// Object factory
// ----------------------------------------------------------------------------

const DEFAULT_VALUES = {
  framebuffer: null,
  copyShader: null,
  tris: null
};

// ----------------------------------------------------------------------------

function extend(publicAPI, model) {
  let initialValues = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
  Object.assign(model, DEFAULT_VALUES, initialValues);

  // Build VTK API
  vtkRenderPass.extend(publicAPI, model, initialValues);
  model.VBOBuildTime = {};
  macro.obj(model.VBOBuildTime, {
    mtime: 0
  });
  model.tris = vtkHelper.newInstance();
  macro.get(publicAPI, model, ['framebuffer']);

  // Object methods
  vtkOpenGLOrderIndependentTranslucentPass(publicAPI, model);
}

// ----------------------------------------------------------------------------

const newInstance = macro.newInstance(extend, 'vtkOpenGLOrderIndependentTranslucentPass');

// ----------------------------------------------------------------------------

var vtkOpenGLOrderIndependentTranslucentPass$1 = {
  newInstance,
  extend
};

export { vtkOpenGLOrderIndependentTranslucentPass$1 as default, extend, newInstance };
