package speiger.src.coreengine.rendering.guiOld;

import org.lwjgl.opengl.GL11;

import speiger.src.coreengine.assets.AssetLocation;
import speiger.src.coreengine.math.vector.ints.Vec2i;
import speiger.src.coreengine.rendering.guiOld.base.DebugOverlay;
import speiger.src.coreengine.rendering.guiOld.renderer.FontRenderer;
import speiger.src.coreengine.rendering.guiOld.renderer.GuiShader;
import speiger.src.coreengine.rendering.guiOld.renderer.UIRenderer;
import speiger.src.coreengine.rendering.guiOld.renderer.provider.FontManager;
import speiger.src.coreengine.rendering.inputOld.events.KeyEvent.CharTypeEvent;
import speiger.src.coreengine.rendering.inputOld.events.KeyEvent.KeyPressEvent;
import speiger.src.coreengine.rendering.inputOld.events.MouseEvent;
import speiger.src.coreengine.rendering.inputOld.window.IWindowListener;
import speiger.src.coreengine.rendering.inputOld.window.ScaledResolution;
import speiger.src.coreengine.rendering.inputOld.window.Window;
import speiger.src.coreengine.rendering.models.buffers.UniformBuffer;
import speiger.src.coreengine.rendering.shaderOld.ShaderTracker;
import speiger.src.coreengine.rendering.utils.Cursor;
import speiger.src.coreengine.rendering.utils.GLStateTracker;
import speiger.src.coreengine.rendering.utils.values.GLBlendFactor;
import speiger.src.coreengine.utils.eventbus.EventBus;
import speiger.src.coreengine.utils.profiler.IProfiler;

public abstract class GuiManager implements IWindowListener
{
	protected GuiBase[] activeGuis = new GuiBase[2];
	protected GuiBase gui;
	protected DebugOverlay debug;
	protected UIRenderer renderer = new UIRenderer(this);
	protected Window window;
	protected ScaledResolution res;
	protected long globalClock = 0L;
	protected boolean isReloading = false;
	protected FontRenderer font;
	protected GuiShader shader = ShaderTracker.INSTANCE.register(GuiShader::create, T -> shader = T);
	
	public GuiManager(Window window, EventBus bus, FontManager manager)
	{
		this.window = window;
		font = manager.loadFont(AssetLocation.of("font/roboto.json"), 18.5F);
		bus.register(MouseEvent.class, this::onMouseEvent);
		bus.register(KeyPressEvent.class, T -> T.setCanceled(onKeyPressed(T.key)));
		bus.register(CharTypeEvent.class, T -> T.setCanceled(onCharTyped(T.character, T.codePoint)));
		window.addListener(this, false);
		debug = createOverlay();
		activeGuis[1] = debug;
		res = window.getUIFrame();
		debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
		debug.onInit();
	}
	
	public abstract IProfiler getGPUProfiler();
	public abstract IProfiler getCPUProfiler();
	public abstract IProfiler getServerProfiler();
	public abstract UniformBuffer getOrthoMatrixBuffer();
	public abstract DebugOverlay createOverlay();
	
	public void showGui(GuiBase base, boolean allowBack)
	{
		if(allowBack)
		{
			base.setLastGui(gui);
		}
		closeGui();
		gui = base;
		if(gui != null)
		{
			gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
			gui.onInit();
			if(activeGuis[0] != null)
			{
				activeGuis[1] = activeGuis[0];
			}
			activeGuis[0] = base;
		}
		if(activeGuis[0] != null)
		{
			activeGuis[0].focus = true;
		}
		if(activeGuis[1] != null)
		{
			activeGuis[1].focus = false;
		}
	}
	
	public void closeGui()
	{
		if(gui != null)
		{
			if(gui == activeGuis[0])
			{
				activeGuis[0] = null;
			}
			if(gui == activeGuis[1])
			{
				activeGuis[1] = null;
			}
			gui.onClosed();
			gui = null;
		}
	}
	
	@Override
	public void onWindowChanged(Window window)
	{
		if(gui != null)
		{
			gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
			gui.onScreenChanged();
		}
		debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
		debug.onScreenChanged();
	}
	
	public void onFixedUpdate()
	{
		globalClock++;
		for(int i = 0;i < 2;i++)
		{
			if(activeGuis[i] != null)
			{
				activeGuis[i].onFixedUpdate();
			}
		}
	}
		
	public void render(float particalTicks)
	{
		Vec2i offsetMouse = start();
		if(debug.isUpdating() || isReloading)
		{
			isReloading = false;
			debug.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
			debug.onScreenChanged();
			if(gui != null)
			{
				gui.setGuiBounds(this, res.getScaledWidth(), res.getScaledHeight());
				gui.onScreenChanged();
			}
		}
		if(activeGuis[0] != null)
		{
			activeGuis[0].baseLayer = activeGuis[1] != null ? activeGuis[1].layers : 0;
		}
		for(int i = 1;i >= 0;i--)
		{
			if(activeGuis[i] != null && (activeGuis[i] instanceof DebugOverlay || debug.isRendering()))
			{
				activeGuis[i].draw(offsetMouse.x(), offsetMouse.y(), particalTicks);
			}
		}
		stop();
		Cursor.INSTANCE.clearCursor(window);
	}
	
	protected Vec2i start()
	{
		GL11.glClear(GL11.GL_DEPTH_BUFFER_BIT);
		GLStateTracker instance = GLStateTracker.instance();
		instance.debth_test.disable();
		instance.cull_face.disable();
		instance.blend.setFunction(GLBlendFactor.SRC_ALPHA, GLBlendFactor.ONE_MINUS_SRC_ALPHA).enable();
		renderer.beginFrame();
		return res.getScaledMouse();
	}
	
	protected void stop()
	{
		GLStateTracker instance = GLStateTracker.instance();
		instance.debth_test.enable();
		renderer.endFrame();
		instance.blend.disable();
		instance.cull_face.disable();
		ShaderTracker.INSTANCE.stopShader();
	}
	
	public boolean onCharTyped(char character, int codePoint)
	{
		for(int i = 0;i < 2;i++)
		{
			if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyTyped(character, codePoint))
			{
				return true;
			}
		}
		return false;
	}
	
	public boolean onKeyPressed(int key)
	{
		for(int i = 0;i < 2;i++)
		{
			if(activeGuis[i] != null && debug.shouldAcceptInputs(activeGuis[i]) && activeGuis[i].onKeyPressed(key))
			{
				return true;
			}
		}
		return false;
	}
	
	public void onMouseEvent(MouseEvent event)
	{
		if(!onMouseEventInternal(event))
		{
			return;
		}
		if(activeGuis[0] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[0]))
		{
			activeGuis[0].onMouseEvent(event);
		}
		if(activeGuis[1] != null && !event.isCanceled() && debug.shouldAcceptInputs(activeGuis[1]))
		{
			activeGuis[1].onMouseEvent(event);
			if(event.isCanceled())
			{
				GuiBase base = activeGuis[0];
				activeGuis[0] = activeGuis[1];
				activeGuis[1] = base;
				if(base != null)
				{
					base.onFocusLost();
					base.baseLayer = 0;
					base.layers = 0;
				}
				if(activeGuis[0] != null)
				{
					activeGuis[0].focus = true;
				}
				if(activeGuis[1] != null)
				{
					activeGuis[1].focus = false;
				}
			}
		}
		event.convertToOrigin();
	}
	
	protected boolean onMouseEventInternal(MouseEvent event)
	{
		event.convertToScreenCoords(res);
		return true;
	}
	
	public boolean isAllowingMovement()
	{
		return gui == null || gui.isAllowingMovement();
	}
	
	public DebugOverlay getDebug()
	{
		return debug;
	}
	
	public boolean isRenderUIBoxes()
	{
		return false;
	}
	
	public GuiShader getShader()
	{
		return shader;
	}
	
	public UIRenderer getRenderer()
	{
		return renderer;
	}
	
	public FontRenderer getFont()
	{
		return font;
	}
	
	public ScaledResolution getRes()
	{
		return res;
	}
}
