package zero1hd.rhythmbullet.graphics.shaders;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.glutils.FrameBuffer;
import com.badlogic.gdx.graphics.glutils.ShaderProgram;
import com.badlogic.gdx.utils.Disposable;
import com.badlogic.gdx.utils.viewport.ScreenViewport;

public class BloomShader implements Disposable {
	private ShaderProgram gaussianBlurShader;
	private ShaderProgram brightFilterShader;
	private ShaderProgram combineShader;
	private FrameBuffer lightFilterBuffer;
	private FrameBuffer normalBuffer;
	private FrameBuffer hBlur, vBlur;
	private TextureRegion fboRegion;
	private Batch screenBatch;
	private ScreenViewport screenViewport;
	
	public BloomShader(Batch batch) {
		this.screenBatch = batch;
		
		Gdx.app.debug("Shader", "Loading glow shaders.");
		screenViewport = new ScreenViewport();
		screenViewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		((OrthographicCamera) screenViewport.getCamera()).setToOrtho(false, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		
		Gdx.app.debug("Shader", "using glow shader");
		brightFilterShader = new ShaderProgram(Gdx.files.internal("shaders/basic.vsh"), Gdx.files.internal("shaders/bright_filter.fsh"));
		if (!brightFilterShader.isCompiled()) {
			Gdx.app.error("Shader failed to compile bright filter shader", brightFilterShader.getLog());
			System.exit(1);
		}
		if (brightFilterShader.getLog().length() != 0) {
			Gdx.app.error("Shader", brightFilterShader.getLog());
		}
			
		gaussianBlurShader = new ShaderProgram(Gdx.files.internal("shaders/basic.vsh"), Gdx.files.internal("shaders/gaussian_blur.fsh"));
		if (!gaussianBlurShader.isCompiled()) {
			Gdx.app.error("Shader failed to compile gaussian blur shader", gaussianBlurShader.getLog());
			System.exit(1);
		}
		if (gaussianBlurShader.getLog().length() != 0) {
			Gdx.app.error("Shader", gaussianBlurShader.getLog());
		}
		
		combineShader = new ShaderProgram(Gdx.files.internal("shaders/basic.vsh"), Gdx.files.internal("shaders/combine.fsh"));
		if (!combineShader.isCompiled()) {
			Gdx.app.error("Shader failed to compile combination shader", combineShader.getLog());
			System.exit(1);
		}
		if (combineShader.getLog().length() != 0) {
			Gdx.app.error("Shader", combineShader.getLog());
		}
		
		lightFilterBuffer = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
		normalBuffer = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
		hBlur = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
		vBlur = new FrameBuffer(Format.RGBA8888, Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
		
		fboRegion = new TextureRegion(normalBuffer.getColorBufferTexture());
		fboRegion.flip(false, true);

		combineShader.begin();
		combineShader.setUniformi("u_texture1", 1);
		combineShader.end();
		
		gaussianBlurShader.begin();
		gaussianBlurShader.setUniformf("radius", 1.5f);
		gaussianBlurShader.setUniformf("resolution", hBlur.getWidth(), vBlur.getHeight());
		gaussianBlurShader.end();
		
		vBlur.getColorBufferTexture().bind(1);
		lightFilterBuffer.getColorBufferTexture().bind(2);
		Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
	}
	
	public void begin() {
//		Begin drawing a normal version of screen
		normalBuffer.begin();
		Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
	}
	
	public void end(float width, float height) {
		normalBuffer.end();

//		BEGINNING NORMAL SCREEN RENDER
		screenViewport.apply();
		
//		Begin light filtering
		lightFilterBuffer.begin();
		Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		fboRegion.setTexture(normalBuffer.getColorBufferTexture());
		screenBatch.setShader(brightFilterShader);
		screenBatch.setProjectionMatrix(screenViewport.getCamera().combined);
		screenBatch.begin(); //SCREEN BATCH STARTS HERE
		screenBatch.draw(fboRegion, 0, 0, width, height);
		screenBatch.flush();
		lightFilterBuffer.end();
		
//		Horizontal gaussian blur
		fboRegion.setTexture(lightFilterBuffer.getColorBufferTexture());
		hBlur.begin();
		screenBatch.setShader(gaussianBlurShader);
		gaussianBlurShader.setUniformi("pass", 0);
		screenBatch.draw(fboRegion, 0f, 0f, width, height);
		screenBatch.flush();
		hBlur.end();
		
		
//		//Vertical gaussian blur
		fboRegion.setTexture(hBlur.getColorBufferTexture());
		vBlur.begin();
		gaussianBlurShader.setUniformi("pass", 1);
		screenBatch.draw(fboRegion, 0f, 0f, width, height);
		screenBatch.flush();
		vBlur.end();
		
		Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		screenBatch.setShader(combineShader);
		fboRegion.setTexture(normalBuffer.getColorBufferTexture());
		screenBatch.draw(fboRegion, 0f, 0f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
		screenBatch.setShader(null);
		screenBatch.end(); //STAGE BATCH ENDS HERE
	}
	
	@Override
	public void dispose() {
		brightFilterShader.dispose();
		combineShader.dispose();
		gaussianBlurShader.dispose();
		normalBuffer.dispose();
		lightFilterBuffer.dispose();
		vBlur.dispose();
		hBlur.dispose();
		
		brightFilterShader = null;
		combineShader = null;
		gaussianBlurShader = null;
		normalBuffer = null;
		lightFilterBuffer = null;
		vBlur = null;
		hBlur = null;
	}
}
