package dev.micle.loginprotection.events.client;

import dev.micle.loginprotection.data.ProtectedPlayer;
import dev.micle.loginprotection.network.NetworkManager;
import dev.micle.loginprotection.network.client.InputPacket;
import dev.micle.loginprotection.proxy.Proxy;
import dev.micle.loginprotection.setup.Config;
import net.minecraft.client.Minecraft;
import net.minecraft.client.Options;
import net.minecraft.client.gui.screens.ChatScreen;
import net.minecraft.client.gui.screens.PauseScreen;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.InventoryScreen;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import org.lwjgl.glfw.GLFW;

import java.util.Arrays;
import java.util.List;

@OnlyIn(Dist.CLIENT)
public class OnClientInputEventHandler {
    private static boolean isPausePressed = false;
    private static boolean isDebugPressed = false;
    private static boolean isFullscreenPressed = false;
    private static boolean isTogglePerspectivePressed = false;
    private static boolean isSmoothCameraPressed = false;
    private static boolean isScreenshotPressed = false;
    private static boolean isSpectatorOutlinesPressed = false;
    private static boolean isAdvancementsPressed = false;
    private static boolean isPlayerListPressed = false;
    private static boolean isChatPressed = false;
    private static boolean isChatCommandPressed = false;
    private static boolean isChatEnterPressed = false;
    private static boolean isSocialInteractionsPressed = false;
    private static boolean isLoadHotbarActivatorPressed = false;
    private static boolean isSaveHotbarActivatorPressed = false;
    private static boolean isSwapOffhandPressed = false;
    private static boolean isInventoryPressed = false;
    private static boolean isDropItemPressed = false;
    private static boolean isUseItemPressed = false;
    private static boolean isPickBlockPressed = false;
    private static boolean isAttackPressed = false;
    private static boolean isMoveUpPressed = false;
    private static boolean isMoveRightPressed = false;
    private static boolean isMoveDownPressed = false;
    private static boolean isMoveLeftPressed = false;
    private static boolean isMoveSprintPressed = false;
    private static boolean isSneakPressed = false;
    private static boolean isJumpPressed = false;
    private static final Boolean[] isHotBarPressed = new Boolean[9];

    private static Screen previousScreen = null;

    public OnClientInputEventHandler() {
        // Initialize isHotBarPressed to false
        Arrays.fill(isHotBarPressed, false);
    }

    @SubscribeEvent
    public void KeyInputEvent(InputEvent.KeyInputEvent event) {
        handle(event.getAction(), event.getKey());
    }

    @SubscribeEvent
    public void MouseInputEvent(InputEvent.MouseInputEvent event) {
        handle(event.getAction(), event.getButton());
    }

    private static void handle(int action, int key) {
        // Initialize variables
        Minecraft minecraft = Minecraft.getInstance();

        // Cancel event if not in a game
        if ((minecraft.getCurrentServer() == null && minecraft.getSingleplayerServer() == null) ||
                Proxy.Client.getPlayerState() == null) {
            return;
        }

        // Check if input is a press or release
        if (action == GLFW.GLFW_PRESS || action == GLFW.GLFW_RELEASE) {
            // Initialize variables
            Options keyBinds = minecraft.options;
            boolean isPressed = action == GLFW.GLFW_PRESS;
            List<String> allowedKeys = (Proxy.Client.getPlayerState().equals(ProtectedPlayer.State.JOINING)) ?
                    (List<String>) Config.Server.LOGIN_KEY_ALLOW_LIST.get() :
                    (List<String>) Config.Server.AFK_KEY_ALLOW_LIST.get();

            // Check if the key is monitored, save it's state, update last input tick and notify server if not allowed
            if (key == GLFW.GLFW_KEY_ESCAPE) {
                isPausePressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.PAUSE.toString())) {
                    updateAndNotify();
                }
            } else if (key == GLFW.GLFW_KEY_F3) {
                isDebugPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.DEBUG.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyFullscreen.getKey().getValue()) {
                isFullscreenPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.FULLSCREEN.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyTogglePerspective.getKey().getValue()) {
                isTogglePerspectivePressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.PERSPECTIVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySmoothCamera.getKey().getValue()) {
                isSmoothCameraPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SMOOTH_CAMERA.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyScreenshot.getKey().getValue()) {
                isScreenshotPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SCREENSHOT.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySpectatorOutlines.getKey().getValue()) {
                isSpectatorOutlinesPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SPECTATOR_OUTLINES.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyAdvancements.getKey().getValue()) {
                isAdvancementsPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.ADVANCEMENTS.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyPlayerList.getKey().getValue()) {
                isPlayerListPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.PLAYER_LIST.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyChat.getKey().getValue()) {
                isChatPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.CHAT.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyCommand.getKey().getValue()) {
                isChatCommandPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.CHAT.toString())) {
                    updateAndNotify();
                }
            } else if (key == GLFW.GLFW_KEY_ENTER) {
                isChatEnterPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.CHAT.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySocialInteractions.getKey().getValue()) {
                isSocialInteractionsPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SOCIAL_INTERACTIONS.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyLoadHotbarActivator.getKey().getValue()) {
                isLoadHotbarActivatorPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.LOAD_HOTBAR_ACTIVATOR.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySaveHotbarActivator.getKey().getValue()) {
                isSaveHotbarActivatorPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SAVE_HOTBAR_ACTIVATOR.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySwapOffhand.getKey().getValue()) {
                isSwapOffhandPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SWAP_ITEM.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyInventory.getKey().getValue()) {
                isInventoryPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.INVENTORY.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyDrop.getKey().getValue()) {
                isDropItemPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.DROP_ITEM.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyUse.getKey().getValue()) {
                isUseItemPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.USE_ITEM.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyPickItem.getKey().getValue()) {
                isPickBlockPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.PICK_BLOCK.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyAttack.getKey().getValue()) {
                isAttackPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.ATTACK.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyUp.getKey().getValue()) {
                isMoveUpPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyRight.getKey().getValue()) {
                isMoveRightPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyDown.getKey().getValue()) {
                isMoveDownPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyLeft.getKey().getValue()) {
                isMoveLeftPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keySprint.getKey().getValue()) {
                isMoveSprintPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyShift.getKey().getValue()) {
                isSneakPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.SNEAK.toString())) {
                    updateAndNotify();
                }
            } else if (key == keyBinds.keyJump.getKey().getValue()) {
                isJumpPressed = isPressed;
                if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.JUMP.toString())) {
                    updateAndNotify();
                }
            } else {
                for (int i = 0; i < isHotBarPressed.length; i++) {
                    if (key == keyBinds.keyHotbarSlots[i].getKey().getValue()) {
                        isHotBarPressed[i] = isPressed;
                        if (!checkIfScreenIsAllowed(allowedKeys, minecraft.screen, !isPressed) &&
                                !allowedKeys.contains(Config.Server.KEYS.HOTBAR.toString())) {
                            updateAndNotify();
                        }
                    }
                }
            }
        }
    }

    private static boolean checkIfScreenIsAllowed(List<String> allowedKeys, Screen screen, boolean isActionReleased) {
        // Initialize variables
        boolean isAllowed;

        // Check if the previous screen or the current screen are significant and their respective keys are allowed
        if (previousScreen instanceof PauseScreen || screen instanceof PauseScreen) {
            isAllowed = allowedKeys.contains(Config.Server.KEYS.PAUSE.toString());
        } else if (previousScreen instanceof InventoryScreen || screen instanceof InventoryScreen) {
            isAllowed = allowedKeys.contains(Config.Server.KEYS.INVENTORY.toString());
        } else if (previousScreen instanceof ChatScreen || screen instanceof ChatScreen) {
            isAllowed = allowedKeys.contains(Config.Server.KEYS.CHAT.toString());
        } else {
            // Allow any other screen that is not the game itself
            isAllowed = screen != null;
        }

        // Only update the previous screen when key is released
        if (isActionReleased) {
            previousScreen = screen;
        }

        // Return value
        return isAllowed;
    }

    private static void updateAndNotify() {
        // Update last input tick
        Proxy.Client.updateLastInputTick();

        // Send an input packet if player is joining or is afk
        if (Proxy.Client.getPlayerState().equals(ProtectedPlayer.State.JOINING) ||
                Proxy.Client.getPlayerState().equals(ProtectedPlayer.State.AFK)) {
            NetworkManager.getChannel().sendToServer(new InputPacket());
        }
    }

    public static void checkIfInputIsAllowed() {
        // Get list of allowed keys
        List<String> allowedKeys = (Proxy.Client.getPlayerState().equals(ProtectedPlayer.State.JOINING)) ?
                (List<String>) Config.Server.LOGIN_KEY_ALLOW_LIST.get() :
                (List<String>) Config.Server.AFK_KEY_ALLOW_LIST.get();

        // Update last input tick and notify server if any of the monitored keys are pressed and not allowed
        if ((isPausePressed && !allowedKeys.contains(Config.Server.KEYS.PAUSE.toString())) ||
                (isDebugPressed && !allowedKeys.contains(Config.Server.KEYS.DEBUG.toString())) ||
                (isFullscreenPressed && !allowedKeys.contains(Config.Server.KEYS.FULLSCREEN.toString())) ||
                (isTogglePerspectivePressed && !allowedKeys.contains(Config.Server.KEYS.PERSPECTIVE.toString())) ||
                (isSmoothCameraPressed && !allowedKeys.contains(Config.Server.KEYS.SMOOTH_CAMERA.toString())) ||
                (isScreenshotPressed && !allowedKeys.contains(Config.Server.KEYS.SCREENSHOT.toString())) ||
                (isSpectatorOutlinesPressed && !allowedKeys.contains(Config.Server.KEYS.SPECTATOR_OUTLINES.toString())) ||
                (isAdvancementsPressed && !allowedKeys.contains(Config.Server.KEYS.ADVANCEMENTS.toString())) ||
                (isPlayerListPressed && !allowedKeys.contains(Config.Server.KEYS.PLAYER_LIST.toString())) ||
                ((isChatPressed || isChatCommandPressed || isChatEnterPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.CHAT.toString())) ||
                (isSocialInteractionsPressed && !allowedKeys.contains(Config.Server.KEYS.SOCIAL_INTERACTIONS.toString())) ||
                (isLoadHotbarActivatorPressed && !allowedKeys.contains(Config.Server.KEYS.LOAD_HOTBAR_ACTIVATOR.toString())) ||
                (isSaveHotbarActivatorPressed && !allowedKeys.contains(Config.Server.KEYS.SAVE_HOTBAR_ACTIVATOR.toString())) ||
                (isSwapOffhandPressed && !allowedKeys.contains(Config.Server.KEYS.SWAP_ITEM.toString())) ||
                (isInventoryPressed && !allowedKeys.contains(Config.Server.KEYS.INVENTORY.toString())) ||
                (isDropItemPressed && !allowedKeys.contains(Config.Server.KEYS.DROP_ITEM.toString())) ||
                (isUseItemPressed && !allowedKeys.contains(Config.Server.KEYS.USE_ITEM.toString())) ||
                (isPickBlockPressed && !allowedKeys.contains(Config.Server.KEYS.PICK_BLOCK.toString())) ||
                (isAttackPressed && !allowedKeys.contains(Config.Server.KEYS.ATTACK.toString())) ||
                ((isMoveUpPressed || isMoveRightPressed || isMoveDownPressed || isMoveLeftPressed || isMoveSprintPressed) &&
                        !allowedKeys.contains(Config.Server.KEYS.MOVE.toString())) ||
                (isSneakPressed && !allowedKeys.contains(Config.Server.KEYS.SNEAK.toString())) ||
                (isJumpPressed && !allowedKeys.contains(Config.Server.KEYS.JUMP.toString())) ||
                (Arrays.stream(isHotBarPressed).anyMatch(b -> b) && !allowedKeys.contains(Config.Server.KEYS.HOTBAR.toString()))) {
            updateAndNotify();
        }
    }
}
