import {
	LinearFilter,
	NearestFilter,
	ShaderMaterial,
	Texture,
	UniformsUtils,
	WebGLRenderTarget
} from 'three';
import { Pass, FullScreenQuad } from './Pass.js';
import { SMAAEdgesShader } from '../shaders/SMAAShader.js';
import { SMAAWeightsShader } from '../shaders/SMAAShader.js';
import { SMAABlendShader } from '../shaders/SMAAShader.js';

class SMAAPass extends Pass {

	constructor( width, height ) {

		super();

		// render targets

		this.edgesRT = new WebGLRenderTarget( width, height, {
			depthBuffer: false
		} );
		this.edgesRT.texture.name = 'SMAAPass.edges';

		this.weightsRT = new WebGLRenderTarget( width, height, {
			depthBuffer: false
		} );
		this.weightsRT.texture.name = 'SMAAPass.weights';

		// textures
		const scope = this;

		const areaTextureImage = new Image();
		areaTextureImage.src = this.getAreaTexture();
		areaTextureImage.onload = function () {

			// assigning data to HTMLImageElement.src is asynchronous (see #15162)
			scope.areaTexture.needsUpdate = true;

		};

		this.areaTexture = new Texture();
		this.areaTexture.name = 'SMAAPass.area';
		this.areaTexture.image = areaTextureImage;
		this.areaTexture.minFilter = LinearFilter;
		this.areaTexture.generateMipmaps = false;
		this.areaTexture.flipY = false;

		const searchTextureImage = new Image();
		searchTextureImage.src = this.getSearchTexture();
		searchTextureImage.onload = function () {

			// assigning data to HTMLImageElement.src is asynchronous (see #15162)
			scope.searchTexture.needsUpdate = true;

		};

		this.searchTexture = new Texture();
		this.searchTexture.name = 'SMAAPass.search';
		this.searchTexture.image = searchTextureImage;
		this.searchTexture.magFilter = NearestFilter;
		this.searchTexture.minFilter = NearestFilter;
		this.searchTexture.generateMipmaps = false;
		this.searchTexture.flipY = false;

		// materials - pass 1

		this.uniformsEdges = UniformsUtils.clone( SMAAEdgesShader.uniforms );

		this.uniformsEdges[ 'resolution' ].value.set( 1 / width, 1 / height );

		this.materialEdges = new ShaderMaterial( {
			defines: Object.assign( {}, SMAAEdgesShader.defines ),
			uniforms: this.uniformsEdges,
			vertexShader: SMAAEdgesShader.vertexShader,
			fragmentShader: SMAAEdgesShader.fragmentShader
		} );

		// materials - pass 2

		this.uniformsWeights = UniformsUtils.clone( SMAAWeightsShader.uniforms );

		this.uniformsWeights[ 'resolution' ].value.set( 1 / width, 1 / height );
		this.uniformsWeights[ 'tDiffuse' ].value = this.edgesRT.texture;
		this.uniformsWeights[ 'tArea' ].value = this.areaTexture;
		this.uniformsWeights[ 'tSearch' ].value = this.searchTexture;

		this.materialWeights = new ShaderMaterial( {
			defines: Object.assign( {}, SMAAWeightsShader.defines ),
			uniforms: this.uniformsWeights,
			vertexShader: SMAAWeightsShader.vertexShader,
			fragmentShader: SMAAWeightsShader.fragmentShader
		} );

		// materials - pass 3

		this.uniformsBlend = UniformsUtils.clone( SMAABlendShader.uniforms );

		this.uniformsBlend[ 'resolution' ].value.set( 1 / width, 1 / height );
		this.uniformsBlend[ 'tDiffuse' ].value = this.weightsRT.texture;

		this.materialBlend = new ShaderMaterial( {
			uniforms: this.uniformsBlend,
			vertexShader: SMAABlendShader.vertexShader,
			fragmentShader: SMAABlendShader.fragmentShader
		} );

		this.needsSwap = false;

		this.fsQuad = new FullScreenQuad( null );

	}

	render( renderer, writeBuffer, readBuffer/*, deltaTime, maskActive*/ ) {

		// pass 1

		this.uniformsEdges[ 'tDiffuse' ].value = readBuffer.texture;

		this.fsQuad.material = this.materialEdges;

		renderer.setRenderTarget( this.edgesRT );
		if ( this.clear ) renderer.clear();
		this.fsQuad.render( renderer );

		// pass 2

		this.fsQuad.material = this.materialWeights;

		renderer.setRenderTarget( this.weightsRT );
		if ( this.clear ) renderer.clear();
		this.fsQuad.render( renderer );

		// pass 3

		this.uniformsBlend[ 'tColor' ].value = readBuffer.texture;

		this.fsQuad.material = this.materialBlend;

		if ( this.renderToScreen ) {

			renderer.setRenderTarget( null );
			this.fsQuad.render( renderer );

		} else {

			renderer.setRenderTarget( writeBuffer );
			if ( this.clear ) renderer.clear();
			this.fsQuad.render( renderer );

		}

	}

	setSize( width, height ) {

		this.edgesRT.setSize( width, height );
		this.weightsRT.setSize( width, height );

		this.materialEdges.uniforms[ 'resolution' ].value.set( 1 / width, 1 / height );
		this.materialWeights.uniforms[ 'resolution' ].value.set( 1 / width, 1 / height );
		this.materialBlend.uniforms[ 'resolution' ].value.set( 1 / width, 1 / height );

	}

	getAreaTexture() {

		return 'data:image/png;base64,[encoded data, 44272 characters]';

	}

	getSearchTexture() {

		return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEIAAAAhCAAAAABIXyLAAAAAOElEQVRIx2NgGAWjYBSMglEwEICREYRgFBZBqDCSLA2MGPUIVQETE9iNUAqLR5gIeoQKRgwXjwAAGn4AtaFeYLEAAAAASUVORK5CYII=';

	}

	dispose() {

		this.edgesRT.dispose();
		this.weightsRT.dispose();

		this.areaTexture.dispose();
		this.searchTexture.dispose();

		this.materialEdges.dispose();
		this.materialWeights.dispose();
		this.materialBlend.dispose();

		this.fsQuad.dispose();

	}

}

export { SMAAPass };
