package zero1hd.rhythmbullet.screens;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputMultiplexer;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Pixmap.Format;
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.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;

import zero1hd.rhythmbullet.RhythmBullet;
import zero1hd.rhythmbullet.audio.MusicDataPack;
import zero1hd.rhythmbullet.audio.map.GamePlayMap;
import zero1hd.rhythmbullet.stages.GameHUD;
import zero1hd.rhythmbullet.stages.GamePlayArea;

public class GameScreen extends ScreenAdapter {
	
	protected GamePlayArea gameArea;
	public GameHUD gameHUD;
	
	protected InputMultiplexer inputs;
	public RhythmBullet core;
	
	private MusicDataPack music;
	
	private ShaderProgram gaussianBlurShader;
	private ShaderProgram brightFilterShader;
	private ShaderProgram combineShader;
	private FrameBuffer lightFilterBuffer;
	private FrameBuffer normalBuffer;
	private FrameBuffer hBlur, vBlur;
	private TextureRegion fboRegion;
	private int fboSize;
	
	/**
	 * The game screen where the game play area, and hud are placed.
	 * @param core The game context object
	 * @param screen the screen to return to if player decides to press "quit" button.
	 */
	public GameScreen(RhythmBullet core, Screen screen) {
		this.core = core;
		
		// Overlay stuff
		ImageButton pause = new ImageButton(core.getDefaultSkin().getDrawable("pause"),
				core.getDefaultSkin().getDrawable("pause-down"));
		pause.setPosition(Gdx.graphics.getWidth() - pause.getWidth() - 25,
				Gdx.graphics.getHeight() - pause.getHeight() - 25);
		pause.addListener(new ChangeListener() {
			@Override
			public void changed(ChangeEvent event, Actor actor) {
				pause();
			}
		});
		
		gameArea = new GamePlayArea(core.getAssetManager(), core.getPrefs());
		gameHUD = new GameHUD(core.getDefaultSkin(), 100f, gameArea, screen, core);
		inputs = new InputMultiplexer();
		inputs.addProcessor(gameHUD);
		inputs.addProcessor(gameArea);
		
	}
	
	public void setGamePlayMap(GamePlayMap gpm) {
		music = gpm.getMusicData();
		gameArea.setAudioMap(gpm);
		gameHUD.setMusic(gpm.getMusicData().getPlaybackMusic());
	}
	
	@Override
	public void show() {
		Gdx.input.setInputProcessor(inputs);
		loadShaders();
		super.show();
	}
	
	/**
	 * needs to be called right after set as screen (should be called in show method).
	 * This is due to the saving preference being done once the screen is hidden.
	 * @param prefs
	 */
	public void loadShaders() {
		if (core.getPrefs().getBoolean("glow shader")) {
			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()) {
				System.err.println(brightFilterShader.getLog());
				System.exit(0);
			}
			if (brightFilterShader.getLog().length() != 0) {
				System.out.println(brightFilterShader.getLog());
			}
				
			gaussianBlurShader = new ShaderProgram(Gdx.files.internal("shaders/basic.vsh"), Gdx.files.internal("shaders/gaussian_blur.fsh"));
			if (!gaussianBlurShader.isCompiled()) {
				System.err.println(gaussianBlurShader.getLog());
				System.exit(0);
			}
			if (gaussianBlurShader.getLog().length() != 0) {
				System.out.println(gaussianBlurShader.getLog());
			}
			
			combineShader = new ShaderProgram(Gdx.files.internal("shaders/basic.vsh"), Gdx.files.internal("shaders/combine.fsh"));
			if (!combineShader.isCompiled()) {
				System.err.println(combineShader.getLog());
				System.exit(0);
			}
			if (combineShader.getLog().length() != 0) {
				System.out.println(combineShader.getLog());
			}
			
			
			if (Gdx.graphics.getWidth() < 1024) {
				fboSize = 2048;
			} else if (Gdx.graphics.getWidth() < 2048) {
				fboSize = 4096;
			} else {
				fboSize = 4096;
			}
			
			lightFilterBuffer = new FrameBuffer(Format.RGBA8888, fboSize/4, fboSize/4, false);
			normalBuffer = new FrameBuffer(Format.RGBA8888, fboSize, fboSize, false);
			hBlur = new FrameBuffer(Format.RGBA8888, fboSize, fboSize, false);
			vBlur = new FrameBuffer(Format.RGBA8888, fboSize, fboSize, false);
			
			fboRegion = new TextureRegion();
			
			combineShader.begin();
			combineShader.setUniformi("u_texture1", 1);
			combineShader.end();
			
			vBlur.getColorBufferTexture().bind(1);
			
			Gdx.gl.glActiveTexture(GL20.GL_TEXTURE0);
		}
		
		ShaderProgram.pedantic = false;
	}
	@Override
	public void render(float delta) {
		Gdx.gl.glClearColor(0f, 0f, 0f, 1f);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
		//actual game and hud
		if (!gameHUD.isPaused()) {
			gameArea.act(delta);
			if (gameArea.getPolyjetEntity().isDead()) {
				end(false);
			}
			gameHUD.act(delta);
		}
		gameArea.getViewport().apply();
		
		if (gaussianBlurShader != null) {
			//Begin drawing a normal version of screen
			normalBuffer.begin();
			Gdx.gl.glClearColor(0f, 0f, 0f, 0f);
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
			gameArea.draw();
			gameArea.getCollisionDetector().renderParticles(gameArea.getBatch(), Gdx.graphics.getDeltaTime(), true);
			normalBuffer.end(
					gameArea.getViewport().getScreenX(), 
					gameArea.getViewport().getScreenY(), 
					gameArea.getViewport().getScreenWidth(), 
					gameArea.getViewport().getScreenHeight());
			//END
			
			//Begin light filtering
			lightFilterBuffer.begin();
			Gdx.gl.glClearColor(0f, 0f, 0f, 0f);
			Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
			gameArea.getBatch().setShader(brightFilterShader);
			gameArea.getBatch().begin(); //BATCH STARTS HERE
			gameArea.getRoot().draw(gameArea.getBatch(), 1f);
			gameArea.getBatch().flush();
			lightFilterBuffer.end(
					gameArea.getViewport().getScreenX(), 
					gameArea.getViewport().getScreenY(), 
					gameArea.getViewport().getScreenWidth(), 
					gameArea.getViewport().getScreenHeight());
			
			//Horizontal gaussian blur
			hBlur.begin();
			fboRegion.setTexture(lightFilterBuffer.getColorBufferTexture());
			gameArea.getBatch().setShader(gaussianBlurShader);
			gaussianBlurShader.setUniformf("horizontal", 1);
			gameArea.getBatch().draw(fboRegion, 0f, 0f, RhythmBullet.GAME_AREA_WIDTH, RhythmBullet.GAME_AREA_HEIGHT);
			gameArea.getBatch().flush();
			hBlur.end(
					gameArea.getViewport().getScreenX(), 
					gameArea.getViewport().getScreenY(), 
					gameArea.getViewport().getScreenWidth(), 
					gameArea.getViewport().getScreenHeight());
			
			//Vertical gaussian blur
			vBlur.begin();
			fboRegion.setTexture(hBlur.getColorBufferTexture());
			gameArea.getBatch().setShader(gaussianBlurShader);
			gaussianBlurShader.setUniformf("horizontal", 0);
			gameArea.getBatch().draw(fboRegion, 0f, 0f, RhythmBullet.GAME_AREA_WIDTH, RhythmBullet.GAME_AREA_HEIGHT);
			gameArea.getBatch().flush();
			vBlur.end(
					gameArea.getViewport().getScreenX(), 
					gameArea.getViewport().getScreenY(), 
					gameArea.getViewport().getScreenWidth(), 
					gameArea.getViewport().getScreenHeight());
			
			//Draw everything to screen
			gameArea.getBatch().setShader(combineShader);
			fboRegion.setRegion(normalBuffer.getColorBufferTexture());
			fboRegion.flip(false, true);
			gameArea.getBatch().draw(fboRegion, 0f, 0f, RhythmBullet.GAME_AREA_WIDTH, RhythmBullet.GAME_AREA_HEIGHT);
			gameArea.getBatch().setShader(null);
			gameArea.getBatch().end(); //BATCH ENDS HERE
		} else {
			gameArea.draw();
		}
		
		gameHUD.getViewport().apply();
		gameHUD.draw();
		if (music != null && !music.getPlaybackMusic().isPlaying()) {
			end(true);
		}
		
		super.render(delta);
	}

	private void end(boolean trueEnd) {
		if (trueEnd) {
			// TODO they didn't die
		} else {
			// TODO They done goofed and ended up dying.
		}
	}

	@Override
	public void dispose() {
		gameHUD.dispose();
		gameArea.dispose();
		
		if (gaussianBlurShader != null) {
			normalBuffer.dispose();
			lightFilterBuffer.dispose();
			hBlur.dispose();
			vBlur.dispose();
			
			brightFilterShader.dispose();
			gaussianBlurShader.dispose();
			combineShader.dispose();
			gaussianBlurShader.dispose();
		}
		super.dispose();
	}
	
	@Override
	public void pause() {
		gameHUD.setPaused(true);
		super.pause();
	}
	
	@Override
	public void resize(int width, int height) {
		gameArea.getViewport().update(width, height, true);
		gameHUD.getViewport().update(width, height, true);

		super.resize(width, height);
	}
	
	public GamePlayArea getGameArea() {
		return gameArea;
	}
	
	public void play() {
		if (music == null) throw new NullPointerException("Idiot, you can't have a music game not have music on the gameplay screen...");
		Gdx.input.setInputProcessor(inputs);
		if (!gameHUD.isPaused()) {
			music.getPlaybackMusic().play();
		}
	}
}