package uk.ashigaru.game;

import java.util.HashMap;
import java.util.Map;

import org.joml.Vector3f;
import org.joml.Vector3i;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL30;

import uk.ashigaru.engine.Engine;
import uk.ashigaru.engine.Frame;
import uk.ashigaru.engine.glfw.Display;
import uk.ashigaru.engine.glfw.Input;
import uk.ashigaru.engine.render.Shader;
import uk.ashigaru.engine.render.model.FBO;
import uk.ashigaru.engine.render.model.Texture;
import uk.ashigaru.engine.scene.Camera;
import uk.ashigaru.engine.util.Col;
import uk.ashigaru.engine.util.Logger;
import uk.ashigaru.engine.util.Resource;
import uk.ashigaru.game.graphics.GUI;
import uk.ashigaru.game.graphics.handlers.BlockSelectionRender;
import uk.ashigaru.game.graphics.handlers.IRenderHandler;
import uk.ashigaru.game.graphics.renderers.ScreenRenderer;
import uk.ashigaru.game.graphics.textures.TextureAtlas;
import uk.ashigaru.game.world.Player;
import uk.ashigaru.game.world.World;
import uk.ashigaru.game.world.block.Block;
import uk.ashigaru.game.world.block.BlockDirt;
import uk.ashigaru.game.world.block.BlockGlass;
import uk.ashigaru.game.world.block.BlockRegistry;
import uk.ashigaru.game.world.block.BlockStone;
import uk.ashigaru.game.world.chunk.IChunk;

public class Game implements Frame {

	public static Game instance;

	private Map<String, IRenderHandler> renderHandlers = new HashMap<String, IRenderHandler>();

	public static void main(String... args) {
		instance = new Game();
		Engine.start(instance, 800, 700, "Game", true, false);
	}
	
	public static Camera getFPCamera() {
		return Game.instance.world.player.getFPCamera();
	}
	
	public static World getWorld() {
		return Game.instance.world;
	}
	
	public static Player getPlayer() {
		return Game.instance.world.player;
	}
	
	public void addRenderHandler(String key, IRenderHandler handler) {
		this.renderHandlers.put(key, handler);
	}
	
	public void removeRenderHandler(String key) {
		this.renderHandlers.remove(key);
	}
	
	public static Block dirt, stone, glass;
	public Vector3f skyColor = new Vector3f(172f / 255f, 221f / 255f, 235f / 255f);
	public World world;
	
	public FBO fbo;
	
	public GUI gui;
	
	public ScreenRenderer sr;

	@Override
	public void init() {
		Logger.log("Starting Game Initialisation");
		Input.setCursorState(GLFW.GLFW_CURSOR_DISABLED);
		
		GL11.glClearColor(skyColor.x, skyColor.y, skyColor.z, 1.0F);
		GL11.glEnable(GL11.GL_CULL_FACE);
		GL11.glCullFace(GL11.GL_BACK);
		GL13.glActiveTexture(GL13.GL_TEXTURE0);

		this.gui = new GUI(new Shader(Resource.find("shaders/gui/gui_Vertex.glsl"), Resource.find("shaders/gui/gui_Fragment.glsl")));

		TextureAtlas.register("default", new TextureAtlas(new Texture(Resource.find("textures/default_atlas.png"))));
		
		Game.dirt = new BlockDirt("dirt").register();
		Game.stone = new BlockStone("stone").register();
		Game.glass = new BlockGlass("glass").register();
		
		Logger.log("Finished Game Initialisation");

		world = new World();
		world.init();
		
		Game.instance.addRenderHandler("blockSelection", new BlockSelectionRender());
		
		sr = new ScreenRenderer();
		
		for(String key : renderHandlers.keySet()) {
			renderHandlers.get(key).init();
		}
		
		Input.onRightClick(() -> {
			Vector3i blockPos = world.raycastLeadingBlock(world.player.getLookRay(), 10, BlockRegistry.air);
			if(blockPos == null) return;
 			int chunkX = World.getChunkCoord(blockPos.x);
			int chunkZ = World.getChunkCoord(blockPos.z);
			if(world.loadedChunks.contains(chunkX, chunkZ)) {
				IChunk chunk = world.loadedChunks.get(chunkX, chunkZ);
				int localX = blockPos.x - chunkX*16;
				int localZ = blockPos.z - chunkZ*16;
				chunk.setBlockAt(localX , blockPos.y, localZ, Math.random() > 2 ? dirt : glass, true);
			}
		}); 
		Input.onLeftClick(() -> {
			Vector3i blockPos = world.raycastBlock(world.player.getLookRay(), 10, BlockRegistry.air);
			if(blockPos == null) return;
			int chunkX = World.getChunkCoord(blockPos.x);
			int chunkZ = World.getChunkCoord(blockPos.z);
			if(world.loadedChunks.contains(chunkX, chunkZ)) {
				IChunk chunk = world.loadedChunks.get(chunkX, chunkZ);
				int localX = blockPos.x - chunkX*16;
				int localZ = blockPos.z - chunkZ*16;
				chunk.setBlockAt(localX , blockPos.y, localZ, BlockRegistry.air, true);
			}
		});
		
		Input.onKeyRelease(GLFW.GLFW_KEY_ESCAPE, () -> {
			int cursorState = Input.getCursorState();
			if(cursorState == GLFW.GLFW_CURSOR_DISABLED) {
				Input.setCursorState(GLFW.GLFW_CURSOR_NORMAL);						
			} else { 
				Input.setCursorState(GLFW.GLFW_CURSOR_DISABLED);						
			}
		}); 
		
		this.fbo = new FBO(Engine.getWidth(), Engine.getHeight());
		this.fbo.bind();
		this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT0, Engine.getWidth(), Engine.getHeight());
		this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT1, Engine.getWidth(), Engine.getHeight());
		this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT2, Engine.getWidth(), Engine.getHeight());
		this.fbo.unbind();
		Engine.eventDisplayResize.attach((x) -> {
			this.fbo.dispose();
			this.fbo = new FBO(Engine.getWidth(), Engine.getHeight()).bind();
			this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT0, Engine.getWidth(), Engine.getHeight());
			this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT1, Engine.getWidth(), Engine.getHeight());
			this.fbo.createColorTexture(GL30.GL_COLOR_ATTACHMENT2, Engine.getWidth(), Engine.getHeight());
			this.fbo.unbind();
		});
	}

	@Override
	public void setupWindow(Display display) {
		display.setResizeable(true);
	}

	@Override
	public void draw(float dt) {
		fbo.bind();
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		fbo.unbind();
		
		world.draw();
		fbo.bind();
		for(String key : renderHandlers.keySet()) {
			renderHandlers.get(key).draw();
		}		
		fbo.unbind();

		sr.render();
		world.drawTransparents();
		
		gui.drawColouredRect(Engine.getWidth()/2 - 4, Engine.getHeight()/2 - 1, 8, 2, Col.or(0xFFFFFF));
		gui.drawColouredRect(Engine.getWidth()/2 - 1, Engine.getHeight()/2 - 4, 2, 8, Col.or(0xFFFFFF));
		
 	}

	@Override
	public void update() {
//		if((world.player.headYaw % 360) > 45 && (world.player.headYaw % 360) < 135) {
//			Logger.log("LOOKING NORTH");
//		} else if((world.player.headYaw % 360) > 135 && (world.player.headYaw % 360) < 225) {
//			Logger.log("LOOKING EAST");
//		} else if((world.player.headYaw % 360) > 225 && (world.player.headYaw % 360) < 315) {
//			Logger.log("LOOKING SOUTH");
//		}  else {
//			Logger.log("LOOKING WEST");
//		}
		world.update();
	}

	@Override
	public void deinit() {
		world.deinit();
		for(String key : renderHandlers.keySet()) {
			renderHandlers.get(key).deinit();
		}

		Logger.log("Deinitialising Game Engine!");
		Logger.saveLogs("log-1.0.0");
	}
}
