package speiger.src.coreengine.rendering.input.devices;

import java.util.function.BiPredicate;

import org.lwjgl.glfw.GLFW;

import speiger.src.coreengine.rendering.input.devices.Keyboard.KeyData;
import speiger.src.coreengine.rendering.input.devices.Keyboard.KeyTask;
import speiger.src.coreengine.rendering.input.events.KeyEvent;
import speiger.src.coreengine.rendering.input.events.KeyEvent.Key;
import speiger.src.coreengine.rendering.input.window.Window;

public class Keyboard extends AbstractDevice<KeyData, KeyTask> {
	public static final Keyboard INSTANCE = new Keyboard();
	@Override
	public void reset(long windowId) {}
	@Override
	protected KeyData createData(long windowId) { return new KeyData(); }
	@Override
	protected void process(KeyTask task) { task.process(this); }
	
	@Override
	protected void registerCallbacks(Window window) {
		super.registerCallbacks(window);
		window.addCallback(this::keyPress, GLFW::glfwSetKeyCallback);
		window.addCallback(this::charTyped, GLFW::glfwSetCharCallback);
	}
	
	private void keyPress(long window, int key, int scancode, int action, int mods) { push(window, new KeyPressed(window, key, scancode, action, mods)); }
	private void charTyped(long window, int codepoint) { push(window, new CharTyped(window, codepoint)); }
	
	public static class KeyData {
		BiPredicate<Integer, Keyboard> filter;
		boolean[] pressedKeys = new boolean[350];
		boolean[] nonConsumedKeys = new boolean[350];
		
		void reset() {
			filter = null;
		}
	}
	
	public static interface KeyTask {
		public void process(Keyboard board);
	}
	
	public record KeyPressed(long window, int key, int scancode, int action, int mods) implements KeyTask {
		@Override
		public void process(Keyboard board) {
			if(key < 0 || key >= 350) return;
			KeyData data = board.get(window);
			if(action >= 1) {
				data.pressedKeys[key] = true;
				onKeyPressed(board, data);
				return;
			}
			data.pressedKeys[key] = false;
			data.nonConsumedKeys[key] = false;
			board.pushEvent(new Key(window, key, scancode, false));
		}
		
		private void onKeyPressed(Keyboard board, KeyData data) {
			if(data.filter == null || data.filter.test(key, board)) {
				if(board.pushEvent(new Key(window, key, scancode, true))) return;
				data.nonConsumedKeys[key] = true;
			}
		}
	}
	
	public record CharTyped(long window, int codepoint) implements KeyTask {
		@Override
		public void process(Keyboard board) {
			board.pushEvent(new KeyEvent.Char(window, codepoint));
		}
	}
}