package speiger.src.coreengine;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.lwjgl.opengl.GL11;
import org.lwjgl.system.Configuration;
import org.lwjgl.util.freetype.FreeType;

import speiger.src.coreengine.assets.api.IAssetPackage;
import speiger.src.coreengine.assets.api.ID;
import speiger.src.coreengine.assets.manager.AssetManager;
import speiger.src.coreengine.core.events.bus.EventBus;
import speiger.src.coreengine.math.vector.matrix.Matrix4f;
import speiger.src.coreengine.platform.graphics.api.buffer.VertexBuffer;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferState;
import speiger.src.coreengine.platform.graphics.api.buffer.states.BufferType;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsCommandBuffer;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsDevice;
import speiger.src.coreengine.platform.graphics.api.core.GraphicsSurface;
import speiger.src.coreengine.platform.graphics.api.mesh.Mesh;
import speiger.src.coreengine.platform.graphics.api.sampler.Sampler;
import speiger.src.coreengine.platform.graphics.api.sampler.SamplerSettings;
import speiger.src.coreengine.platform.graphics.api.sampler.states.BorderMode;
import speiger.src.coreengine.platform.graphics.api.sampler.states.SampleMode;
import speiger.src.coreengine.platform.graphics.api.shader.ShaderPipeline;
import speiger.src.coreengine.platform.graphics.api.shader.states.DrawMode;
import speiger.src.coreengine.platform.graphics.api.shader.states.GraphicsDataType;
import speiger.src.coreengine.platform.graphics.api.shader.states.ShaderType;
import speiger.src.coreengine.platform.graphics.api.texture.Texture;
import speiger.src.coreengine.platform.graphics.api.texture.TextureSettings;
import speiger.src.coreengine.platform.graphics.api.texture.drawable.Drawable;
import speiger.src.coreengine.platform.graphics.api.texture.states.TextureFormat;
import speiger.src.coreengine.platform.graphics.api.texture.states.TextureType;
import speiger.src.coreengine.platform.graphics.api.utils.ExecutionType;
import speiger.src.coreengine.platform.graphics.api.utils.SampledTexture;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout;
import speiger.src.coreengine.platform.graphics.api.vertex.VertexLayout.Usage;
import speiger.src.coreengine.platform.graphics.api.vertex.builder.VertexBuilder;
import speiger.src.coreengine.platform.graphics.opengl.core.GLGraphics;
import speiger.src.coreengine.platform.input.device.FileDrop;
import speiger.src.coreengine.platform.input.device.Joystick;
import speiger.src.coreengine.platform.input.device.Keyboard;
import speiger.src.coreengine.platform.input.device.Mouse;
import speiger.src.coreengine.platform.input.window.Window;
import speiger.src.coreengine.platform.input.window.WindowManager;
import speiger.src.coreengine.utils.helpers.IOUtils;

public class NewRenderEngineTest {
	EventBus bus = EventBus.builder().build();
	WindowManager manager = new WindowManager();
	AssetManager assets = AssetManager.single(IAssetPackage.asset(IOUtils.getBaseLocation()));
	
	public static void main(String... args) {
		Configuration.HARFBUZZ_LIBRARY_NAME.set(FreeType.getLibrary());
		NewRenderEngineTest test = new NewRenderEngineTest();
		test.manager.setup(test::setup, new GLGraphics(test.assets));
	}
	
	public void setup() {
		Mouse.INSTANCE.init(bus);
		Keyboard.INSTANCE.init(bus);
		Joystick.INSTANCE.init(manager, bus);
		FileDrop.INSTANCE.init(bus);
		manager.addDevices(Mouse.INSTANCE, Keyboard.INSTANCE, Joystick.INSTANCE, FileDrop.INSTANCE);
		manager.builder().title("Testing Engine").width(800).height(600).antialis(0).build().setupContext(this::run);
	}
	
	public void run() {
		Thread.currentThread().setName("Main Thread");
		Window window = Window.SCOPE.get();
		GraphicsDevice device = GraphicsDevice.SCOPE.get();
		int size = 512;
		int half = size >> 1;
		Drawable drawer = new Drawable(TextureFormat.RGBA, size, size);
		drawer.fill(0, 0, size, size, -1);
		drawer.fill(0, 0, half, half, 255, 0, 0, 255);
		drawer.fill(half, 0, half, half, 0, 255, 0, 255);
		drawer.fill(half, half, half, half, 255, 0, 0, 255);
		drawer.fill(0, half, half, half, 0, 0, 255, 255);
		
		VertexLayout layout = VertexLayout.builder()
				.element("pos", 0, 3, Usage.POS)
				.element("tex", 1, 2, Usage.UV)
				.element("color", 2, 4, Usage.COLOR, GraphicsDataType.UNSIGNED_BYTE, true)
				.build();
		
		VertexBuilder builder = new VertexBuilder(255);
		builder.start(DrawMode.TRIANGLES, layout);
		builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1);
		builder.pos(0.5F, -0.5F, 0).tex(1F, 1F).rgba(-1);
		builder.pos(0.5F, 0.5F, 0).tex(1F, 0F).rgba(-1);
		builder.pos(0.5F, 0.5F, 0).tex(1F, 0F).rgba(-1);
		builder.pos(-0.5F, 0.5F, 0).tex(0F, 0F).rgba(-1);
		builder.pos(-0.5F, -0.5F, 0).tex(0F, 1F).rgba(-1);
		
		Texture texture = device.createTexture(TextureSettings.builder().build(), size, size);
		device.queue().writeToTexture(texture, TextureFormat.RGBA, GraphicsDataType.UNSIGNED_BYTE, drawer.pixels());
		drawer.close();
		

		
		Mesh mesh = device.createMesh(Mesh.builder().layout(layout));
		mesh.buffer(0, device.createBuffer(BufferType.ARRAY_BUFFER, BufferState.STATIC_DRAW).bind().set(builder.bytes()).unbind());
		builder.close();
		
		Sampler sampler = device.createSampler(SamplerSettings.builder().sampler(SampleMode.NEAREST).wrap(BorderMode.CLAMP_TO_EDGE).build());
		VertexBuffer buffer = device.createBuffer(BufferType.UNIFORM_BUFFER, BufferState.STATIC_DRAW);
		ByteBuffer mat = ByteBuffer.allocate(64).order(ByteOrder.nativeOrder());
		int scale = 0;
		new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
		buffer.bind().set(mat.flip().array()).unbind();
		
		
		ShaderPipeline pipeline = ShaderPipeline.builder(ID.of("gui"))
			.withStage(ShaderType.VERTEX, ID.of("shader/newTestGui/vertex.vs"))
			.withStage(ShaderType.FRAGMENT, ID.of("shader/newTestGui/fragment.fs"))
			.withTexture("texture", 0, 0, TextureType.TEXTURE_2D)
			.withUniform("Camera", 0, 0, 64)
			.withFormat(layout)
			.build();
		window.visible(true);
		GraphicsCommandBuffer queue = device.createCommandBuffer(ExecutionType.RECORDED);

		queue.begin()
			.pipeline(pipeline)
			.mesh(mesh)
			.texture(0, new SampledTexture(texture, sampler))
			.uniform(0, buffer)
			.drawArrays(0, 6)
			.end();
		
		GL11.glViewport(0, 0, window.width(), window.height());
		
		GraphicsSurface surface = window.surface();
		while(!window.shouldClose()) {
			surface.beginFrame();
			if(window.changed()) {
				window.updateViewport();
				GL11.glViewport(0, 0, window.width(), window.height());
				new Matrix4f().ortho(0, 0, window.width() >> scale, window.height() >> scale, 1000, -1000).store(mat);
				buffer.bind().set(mat.flip().array()).unbind();
			}
			
			device.queue().submitCommands(queue);
			device.queue().submitFrame();
			surface.finishFrame();
			
			try {
				Thread.sleep(10);
			}
			catch(Exception e) {
				e.printStackTrace();
			}
		}
		window.destroy();
		manager.shutdown();
	}
}
