package electrosphere.controls;

import static org.lwjgl.glfw.GLFW.GLFW_CURSOR;
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED;
import static org.lwjgl.glfw.GLFW.GLFW_CURSOR_NORMAL;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_0;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_1;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_2;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_3;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_4;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_5;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_6;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_7;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_8;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_9;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_A;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_B;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_BACKSPACE;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_C;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_CAPS_LOCK;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_D;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_DOWN;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_E;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_ENTER;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_ESCAPE;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_F;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_F2;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_F24;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_F4;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_G;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_H;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_I;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_J;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_K;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_L;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_CONTROL;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_LEFT_SHIFT;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_M;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_N;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_O;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_P;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_PERIOD;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_Q;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_R;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_S;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_SLASH;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_SPACE;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_T;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_U;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_UP;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_V;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_W;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_X;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_Y;
import static org.lwjgl.glfw.GLFW.GLFW_KEY_Z;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_1;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_2;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_LEFT;
import static org.lwjgl.glfw.GLFW.GLFW_MOUSE_BUTTON_RIGHT;
import static org.lwjgl.glfw.GLFW.glfwGetCursorPos;
import static org.lwjgl.glfw.GLFW.glfwSetInputMode;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;

import org.joml.Vector2f;
import org.joml.Vector3d;
import org.joml.Vector3f;

import electrosphere.audio.VirtualAudioSourceManager.VirtualAudioSourceType;
import electrosphere.client.item.ItemActions;
import electrosphere.client.targeting.crosshair.Crosshair;
import electrosphere.client.terrain.editing.TerrainEditing;
import electrosphere.collision.CollisionEngine;
import electrosphere.controls.Control.ControlMethod;
import electrosphere.controls.Control.ControlType;
import electrosphere.engine.Globals;
import electrosphere.engine.Main;
import electrosphere.entity.Entity;
import electrosphere.entity.btree.BehaviorTree;
import electrosphere.entity.state.attack.ClientAttackTree;
import electrosphere.entity.state.attack.ShooterTree;
import electrosphere.entity.state.equip.ClientEquipState;
import electrosphere.entity.state.inventory.InventoryUtils;
import electrosphere.entity.state.inventory.UnrelationalInventoryState;
import electrosphere.entity.state.movement.SprintTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementRelativeFacing;
import electrosphere.entity.state.movement.groundmove.ClientGroundMovementTree.MovementTreeState;
import electrosphere.entity.state.movement.jump.ClientJumpTree;
import electrosphere.entity.types.camera.CameraEntityUtils;
import electrosphere.entity.types.creature.CreatureUtils;
import electrosphere.logger.LoggerInterface;
import electrosphere.menu.WindowStrings;
import electrosphere.menu.WindowUtils;
import electrosphere.menu.debug.ImGuiWindowMacros;
import electrosphere.menu.ingame.MenuGeneratorsInGame;
import electrosphere.menu.ingame.MenuGeneratorsInventory;
import electrosphere.renderer.RenderingEngine;
import electrosphere.renderer.ui.elements.Window;
import electrosphere.renderer.ui.events.ClickEvent;
import electrosphere.renderer.ui.events.KeyboardEvent;
import electrosphere.renderer.ui.events.MenuEvent;
import electrosphere.renderer.ui.events.MenuEvent.MenuEventType;
import electrosphere.util.FileUtils;
import electrosphere.renderer.ui.events.MouseEvent;
import electrosphere.renderer.ui.events.ScrollEvent;

/**
 * Main handler for controls
 */
public class ControlHandler {
    
    //main game
    public static final String INPUT_CODE_CAMERA_ROTATION = "cameraRotation";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD = "moveForward";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD = "moveBackward";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_LEFT = "moveLeft";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT = "moveRight";
    public static final String DATA_STRING_INPUT_CODE_STRAFE_LEFT = "strafeLeft";
    public static final String DATA_STRING_INPUT_CODE_STRAFE_RIGHT = "strafeRight";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_JUMP = "jump";
    public static final String DATA_STRING_INPUT_CODE_MOVEMENT_FALL = "fall";
    public static final String DATA_STRING_INPUT_CODE_ATTACK_PRIMARY = "attackPrimary";
    public static final String DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM = "debugSpawnItem";
    public static final String DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU = "inGameMainMenu";
    public static final String DATA_STRING_INPUT_CODE_LOCK_CROSSHAIR = "crosshairLock";
    public static final String INPUT_CODE_SPRINT = "sprint";
    public static final String INPUT_CODE_INTERACT = "interact";
    public static final String INPUT_CODE_DROP = "drop";
    public static final String INPUT_CODE_INVENTORY_OPEN = "inventoryOpen";
    public static final String INPUT_CODE_CHARACTER_OPEN = "characterOpen";
    public static final String ITEM_SECONDARY = "actionItemSecondary";
    public static final String INPUT_CODE_PLACE_TERRAIN = "placeTerrain";
    public static final String INPUT_CODE_REMOVE_TERRAIN = "removeTerrain";
    

    //menu navigation
    public static final String DATA_STRING_INPUT_CODE_MENU_NAVIGATE_FORWARD = "menuNavigateForward";
    public static final String DATA_STRING_INPUT_CODE_MENU_NAVIGATE_BACKWARDS = "menuNavigateBackwards";
    public static final String DATA_STRING_INPUT_CODE_MENU_INCREMENT = "menuIncrement";
    public static final String DATA_STRING_INPUT_CODE_MENU_DECREMENT = "menuDecrement";
    public static final String DATA_STRING_INPUT_CODE_MENU_SELECT = "menuSelect";
    public static final String INPUT_CODE_MENU_MOUSE_PRIMARY = "menuMousePrimary";
    public static final String DATA_STRING_INPUT_CODE_MENU_BACKOUT = "menuBackout";
    public static final String MENU_MOUSE_MOVE = "menuMouseMove";
    public static final String MENU_SCROLL = "menuScroll";
    public static final String MENU_DRAG_MANIPULATE = "menuDragManipulate";
    public static final String MENU_DRAG_START = "menuDragStart";
    public static final String MENU_CAPTURE_SCREEN = "menuCaptureScreen";
    
    
    
    //typing
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_BACKSPACE = "menuTypeBackspace";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_0 = "menuType0";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_1 = "menuType1";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_2 = "menuType2";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_3 = "menuType3";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_4 = "menuType4";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_5 = "menuType5";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_6 = "menuType6";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_7 = "menuType7";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_8 = "menuType8";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_9 = "menuType9";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_PERIOD = "menuType.";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_A = "menuTypeA";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_B = "menuTypeB";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_C = "menuTypeC";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_D = "menuTypeD";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_E = "menuTypeE";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_F = "menuTypeF";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_G = "menuTypeG";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_H = "menuTypeH";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_I = "menuTypeI";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_J = "menuTypeJ";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_K = "menuTypeK";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_L = "menuTypeL";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_M = "menuTypeM";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_N = "menuTypeN";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_O = "menuTypeO";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_P = "menuTypeP";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_Q = "menuTypeQ";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_R = "menuTypeR";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_S = "menuTypeS";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_T = "menuTypeT";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_U = "menuTypeU";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_V = "menuTypeV";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_W = "menuTypeW";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_X = "menuTypeX";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_Y = "menuTypeY";
    public static final String DATA_STRING_INPUT_CODE_MENU_TYPE_Z = "menuTypeZ";
    public static final String INPUT_CODE_MENU_TYPE_FORWARD_SLASH = "menuTypeForwardSlash";

    //inventory
    public static final String INPUT_CODE_INVENTORY_CLOSE = "inventoryClose";
    public static final String INPUT_CODE_INVENTORY_ITEM_MANIPULATE = "inventoryItemManipulate";
    public static final String INPUT_CODE_INVENTORY_ITEM_DRAG = "inventoryDrag";

    //debug
    public static final String DEBUG_FRAMESTEP = "framestep";
    public static final String DEBUG_OPEN_DEBUG_MENU = "openDebugMenu";

    //freecam
    public static final String FREECAM_UP = "freecamUp";
    public static final String FREECAM_DOWN = "freecamDown";
    public static final String FREECAM_FORWARD = "freecamForward";
    public static final String FREECAM_BACKWARD = "freecamBackward";
    public static final String FREECAM_LEFT = "freecamLeft";
    public static final String FREECAM_RIGHT = "freecamRight";
    public static final String FREECAM_MOUSE = "freecamMouse";
    
    /**
     * The different buckets of inputs that the control handler be configured to scan for each frame
     */
    public static enum ControlsState {
        TITLE_PAGE,
        TITLE_MENU,
        MAIN_GAME,
        IN_GAME_MAIN_MENU,
        IN_GAME_FREE_CAMERA,
        INVENTORY,
        NO_INPUT,
    }
    
    //The bucket of inputs that the control handler is currently scanning for
    ControlsState state = ControlsState.TITLE_MENU;


    /**
     * The list of control states that have the mouse visible and enabled
     */
    static ControlsState[] mouseEnabledStates = new ControlsState[]{
        ControlsState.TITLE_PAGE,
        ControlsState.TITLE_MENU,
        ControlsState.IN_GAME_MAIN_MENU,
        ControlsState.INVENTORY,
    };
    

    //controls whether the mouse is visible or not
    boolean mouseIsVisible = true;

    //if set to true, opengl will try to capture the screen next frame
    boolean shouldRecaptureScreen = false;

    //controls whether the camera is first or third person
    boolean cameraIsThirdPerson = false;

    //The list of window strings that would block main game controls
    static String[] controlBlockingWindows = new String[]{
        WindowStrings.LEVEL_EDTIOR_SIDE_PANEL,
        WindowStrings.VOXEL_TYPE_SELECTION,
        WindowStrings.WINDOW_CHARACTER,
        WindowStrings.WINDOW_DEBUG,
        WindowStrings.WINDOW_MENU_INGAME_MAIN,
        WindowStrings.WINDOW_MENU_INVENTORY,
    };


    /*
    Mouse event parsing related stuff
    */
    float mouse_lastX = 400;
    float mouse_lastY = 300;
    double xpos = 400;
    double ypos = 300;
    double mouse_X_Buffer[] = new double[1];
    double mouse_Y_Buffer[] = new double[1];
    boolean dragging = false; //tracks whether the mouse is doing a drag input or not


    //The map of data string -> control object
    HashMap<String, Control> controls;

    List<Control> mainGameControlList = new LinkedList<Control>();
    List<Control> mainGameDebugControlList = new LinkedList<Control>();
    List<Control> menuNavigationControlList = new LinkedList<Control>();
    List<Control> typingControlList = new LinkedList<Control>();
    List<Control> inventoryControlList = new LinkedList<Control>();
    List<Control> alwaysOnDebugControlList = new LinkedList<Control>();
    List<Control> freeCameraControlList = new LinkedList<Control>();
    
    /**
     * Constructor
     */
    ControlHandler(){
        controls = new HashMap<String, Control>();
    }
    
    /**
     * Generates an example controls map
     * @return the example controls map object
     */
    public static ControlHandler generateExampleControlsMap(){
        ControlHandler handler = new ControlHandler();
        /*
        Main Game controls
        */
        
        /*
        Map the controls
        */
        handler.addControl(INPUT_CODE_CAMERA_ROTATION, new Control(ControlType.MOUSE_MOVEMENT,0,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD, new Control(ControlType.KEY,GLFW_KEY_W,false,"Move Foward","Moves the player forward"));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD, new Control(ControlType.KEY,GLFW_KEY_S,false,"Move Backward","Moves the player backward"));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_LEFT, new Control(ControlType.KEY,GLFW_KEY_F24,false,"Move Left","Moves the player left"));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT, new Control(ControlType.KEY,GLFW_KEY_F24,false,"Move Right","Moves the player right"));
        handler.addControl(DATA_STRING_INPUT_CODE_STRAFE_LEFT, new Control(ControlType.KEY,GLFW_KEY_A,false,"Strafe Left","Strafes the player left"));
        handler.addControl(DATA_STRING_INPUT_CODE_STRAFE_RIGHT, new Control(ControlType.KEY,GLFW_KEY_D,false,"Strafe Right","Strafes the player right"));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_JUMP, new Control(ControlType.KEY,GLFW_KEY_SPACE,false,"Jump","Jumps"));
        handler.addControl(DATA_STRING_INPUT_CODE_MOVEMENT_FALL, new Control(ControlType.KEY,GLFW_KEY_LEFT_CONTROL,false,"Fall","Lowers the camera"));
        handler.addControl(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_LEFT,false,"Attack","Attacks"));
        handler.addControl(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU, new Control(ControlType.KEY,GLFW_KEY_ESCAPE,false,"Main Menu","Opens the main menu while in game"));
        handler.addControl(DATA_STRING_INPUT_CODE_LOCK_CROSSHAIR, new Control(ControlType.KEY,GLFW_KEY_CAPS_LOCK,false,"Lock On","Locks the camera onto the target"));
        handler.addControl(INPUT_CODE_SPRINT, new Control(ControlType.KEY,GLFW_KEY_LEFT_SHIFT,false,"Sprint (hold)","Causes the player to sprint"));
        handler.addControl(INPUT_CODE_INTERACT, new Control(ControlType.KEY,GLFW_KEY_E,false,"Interact","Interacts with whatever is targeted currently"));
        handler.addControl(INPUT_CODE_DROP, new Control(ControlType.KEY,GLFW_KEY_Y,false,"Drop","Drops the currently equipped item"));
        handler.addControl(INPUT_CODE_INVENTORY_OPEN, new Control(ControlType.KEY,GLFW_KEY_I,false,"Inventory","Opens the player's inventory"));
        handler.addControl(INPUT_CODE_CHARACTER_OPEN, new Control(ControlType.KEY,GLFW_KEY_C,false,"Equip Menu","Opens the player's equipment menu"));
        handler.addControl(ITEM_SECONDARY, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_RIGHT,false,"Secondary","Uses the secondary equipped item"));
                
        /*
        Map the menu navigation controls
        */
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_FORWARD, new Control(ControlType.KEY,GLFW_KEY_DOWN,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_BACKWARDS, new Control(ControlType.KEY,GLFW_KEY_UP,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_INCREMENT, new Control(ControlType.KEY,GLFW_KEY_RIGHT,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_DECREMENT, new Control(ControlType.KEY,GLFW_KEY_LEFT,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_SELECT, new Control(ControlType.KEY,GLFW_KEY_ENTER,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_BACKOUT, new Control(ControlType.KEY,GLFW_KEY_ESCAPE,false,"",""));
        handler.addControl(MENU_MOUSE_MOVE, new Control(ControlType.MOUSE_MOVEMENT,0,false,"",""));
        handler.addControl(INPUT_CODE_MENU_MOUSE_PRIMARY, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_LEFT,false,"",""));
        handler.addControl(MENU_SCROLL, new Control(ControlType.MOUSE_SCROLL,0,false,"",""));
        handler.addControl(MENU_DRAG_START, new Control(ControlType.MOUSE_MOVEMENT,0,false,"",""));
        handler.addControl(MENU_CAPTURE_SCREEN, new Control(ControlType.KEY,GLFW_KEY_F4,true,"Screenshot","Takes a screenshot of the engine"));
        handler.addControl(MENU_DRAG_MANIPULATE, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_1,false,"",""));
        
        /*
        Map the typing controls
        */
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_BACKSPACE, new Control(ControlType.KEY,GLFW_KEY_BACKSPACE,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_0, new Control(ControlType.KEY,GLFW_KEY_0,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_1, new Control(ControlType.KEY,GLFW_KEY_1,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_2, new Control(ControlType.KEY,GLFW_KEY_2,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_3, new Control(ControlType.KEY,GLFW_KEY_3,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_4, new Control(ControlType.KEY,GLFW_KEY_4,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_5, new Control(ControlType.KEY,GLFW_KEY_5,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_6, new Control(ControlType.KEY,GLFW_KEY_6,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_7, new Control(ControlType.KEY,GLFW_KEY_7,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_8, new Control(ControlType.KEY,GLFW_KEY_8,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_9, new Control(ControlType.KEY,GLFW_KEY_9,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_PERIOD, new Control(ControlType.KEY,GLFW_KEY_PERIOD,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_A, new Control(ControlType.KEY,GLFW_KEY_A,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_B, new Control(ControlType.KEY,GLFW_KEY_B,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_C, new Control(ControlType.KEY,GLFW_KEY_C,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_D, new Control(ControlType.KEY,GLFW_KEY_D,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_E, new Control(ControlType.KEY,GLFW_KEY_E,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_F, new Control(ControlType.KEY,GLFW_KEY_F,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_G, new Control(ControlType.KEY,GLFW_KEY_G,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_H, new Control(ControlType.KEY,GLFW_KEY_H,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_I, new Control(ControlType.KEY,GLFW_KEY_I,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_J, new Control(ControlType.KEY,GLFW_KEY_J,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_K, new Control(ControlType.KEY,GLFW_KEY_K,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_L, new Control(ControlType.KEY,GLFW_KEY_L,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_M, new Control(ControlType.KEY,GLFW_KEY_M,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_N, new Control(ControlType.KEY,GLFW_KEY_N,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_O, new Control(ControlType.KEY,GLFW_KEY_O,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_P, new Control(ControlType.KEY,GLFW_KEY_P,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_Q, new Control(ControlType.KEY,GLFW_KEY_Q,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_R, new Control(ControlType.KEY,GLFW_KEY_R,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_S, new Control(ControlType.KEY,GLFW_KEY_S,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_T, new Control(ControlType.KEY,GLFW_KEY_T,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_U, new Control(ControlType.KEY,GLFW_KEY_U,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_V, new Control(ControlType.KEY,GLFW_KEY_V,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_W, new Control(ControlType.KEY,GLFW_KEY_W,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_X, new Control(ControlType.KEY,GLFW_KEY_X,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_Y, new Control(ControlType.KEY,GLFW_KEY_Y,false,"",""));
        handler.addControl(DATA_STRING_INPUT_CODE_MENU_TYPE_Z, new Control(ControlType.KEY,GLFW_KEY_Z,false,"",""));
        handler.addControl(INPUT_CODE_MENU_TYPE_FORWARD_SLASH, new Control(ControlType.KEY,GLFW_KEY_SLASH,false,"",""));

        /*
        Inventory controls
        */
        handler.addControl(INPUT_CODE_INVENTORY_CLOSE, new Control(ControlType.KEY,GLFW_KEY_I,false,"",""));
        handler.addControl(INPUT_CODE_INVENTORY_ITEM_MANIPULATE, new Control(ControlType.MOUSE_BUTTON,GLFW_MOUSE_BUTTON_1,false,"",""));
        handler.addControl(INPUT_CODE_INVENTORY_ITEM_DRAG, new Control(ControlType.MOUSE_MOVEMENT,0,false,"",""));

        /**
        Terrain controls
         */
        handler.addControl(INPUT_CODE_PLACE_TERRAIN, new Control(ControlType.KEY,GLFW_KEY_T,false,"Place Terrain","Places terrain"));
        handler.addControl(INPUT_CODE_REMOVE_TERRAIN, new Control(ControlType.KEY,GLFW_KEY_Y,false,"Remove Terrain","Removes terrain"));

        /*
        framestep controls
        */
        handler.addControl(DEBUG_FRAMESTEP, new Control(ControlType.KEY, GLFW_KEY_P,false,"Framesetp","Steps the engine forward one frame"));

        /*
         * Free camera
         */
        handler.addControl(FREECAM_UP,       new Control(ControlType.KEY,GLFW_KEY_SPACE,false,"Up","Moves the camera up"));
        handler.addControl(FREECAM_DOWN,     new Control(ControlType.KEY,GLFW_KEY_LEFT_CONTROL,false,"Down","Moves the camera down"));
        handler.addControl(FREECAM_FORWARD,  new Control(ControlType.KEY,GLFW_KEY_W,false,"Forward","Moves the camera forward"));
        handler.addControl(FREECAM_BACKWARD, new Control(ControlType.KEY,GLFW_KEY_S,false,"Backward","Moves the camera backward"));
        handler.addControl(FREECAM_LEFT,     new Control(ControlType.KEY,GLFW_KEY_A,false,"Left","Moves the camera left"));
        handler.addControl(FREECAM_RIGHT,    new Control(ControlType.KEY,GLFW_KEY_D,false,"Right","Moves the camera right"));
        handler.addControl(FREECAM_MOUSE,    new Control(ControlType.MOUSE_MOVEMENT,0,false,"",""));

        
        /*
        Save to file
        */
//        Utilities.saveObjectToBakedJsonFile("/Config/keybinds.json", handler);
        
        /*
        Debug controls
        */
        handler.addControl(DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM, new Control(ControlType.KEY,GLFW_KEY_Q,false,"",""));
        handler.addControl(DEBUG_OPEN_DEBUG_MENU, new Control(ControlType.KEY, GLFW_KEY_F2,false,"Debug Menu","Opens the debug menu"));

        /*
        return
        */
        return handler;
    }


    
    
    /**
     * Polls the currently set bucket of controls
     */
    public void pollControls(){
        switch(state){
            
            
            case MAIN_GAME:
            runHandlers(mainGameControlList);
            runHandlers(mainGameDebugControlList);
            runHandlers(alwaysOnDebugControlList);
            break;
                
                
                
            case TITLE_PAGE:
            break;
                
                
            case TITLE_MENU:
            runHandlers(menuNavigationControlList);
            /*
            Typing..
            */
            runHandlers(typingControlList);
            runHandlers(alwaysOnDebugControlList);
            break;
                
            case IN_GAME_MAIN_MENU:
            runHandlers(menuNavigationControlList);
            runHandlers(typingControlList);
            runHandlers(alwaysOnDebugControlList);
            // pollMenuNavigationControls();
            break;

            case IN_GAME_FREE_CAMERA:
            runHandlers(freeCameraControlList);
            runHandlers(mainGameDebugControlList);
            runHandlers(alwaysOnDebugControlList);
            break;

            case INVENTORY:
            runHandlers(inventoryControlList);
            runHandlers(menuNavigationControlList);
            runHandlers(alwaysOnDebugControlList);
            break;
                
            case NO_INPUT:
            runHandlers(alwaysOnDebugControlList);
            break;
                
        }
        Globals.scrollCallback.clear();
    }

    /**
     * Attaches callbacks to each of the control objects
     */
    public void setCallbacks(){
        setMainGameControls();
        setInGameDebugControls();
        setMenuNavigationControls();
        setTypingControls();
        setInventoryControls();
        setAlwaysOnDebugControls();
        setFreecamControls();
    }
    

    /**
     * Sets callbacks for the main game controls
     */
    void setMainGameControls(){
        /*
        Camera rotation
        */
        mainGameControlList.add(controls.get(INPUT_CODE_CAMERA_ROTATION));
        controls.get(INPUT_CODE_CAMERA_ROTATION).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent event){
            Globals.cameraHandler.handleMouseEvent(event);
        }});
        /*
        Move forward
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD));
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD_LEFT);
                    } else if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD_RIGHT);
                    } else {
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD_LEFT);
                    } else if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD_RIGHT);
                    } else {
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});
        /*
        Move backward
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD));
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(3.0/4.0*Math.PI).normalize());
                        groundTree.start(MovementRelativeFacing.BACKWARD_LEFT);
                    } else if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(5.0/4.0*Math.PI).normalize());
                        groundTree.start(MovementRelativeFacing.BACKWARD_RIGHT);
                    } else {
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.BACKWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(3.0/4.0*Math.PI).normalize());
                        groundTree.start(MovementRelativeFacing.BACKWARD_LEFT);
                    } else if(controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).isState()){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(5.0/4.0*Math.PI).normalize());
                        groundTree.start(MovementRelativeFacing.BACKWARD_RIGHT);
                    } else {
                        CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                        groundTree.start(MovementRelativeFacing.BACKWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});
        /*
        move left
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_LEFT));
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_LEFT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(Math.PI/2.0).normalize());
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_LEFT).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(Math.PI/2.0).normalize());
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_LEFT).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});
        /*
        move right
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT));
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(-Math.PI/2.0).normalize());
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                    if(
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        CreatureUtils.setFacingVector(Globals.playerEntity, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).rotateY(-Math.PI/2.0).normalize());
                        groundTree.start(MovementRelativeFacing.FORWARD);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_RIGHT).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});

        /*
        Move up
        */

        /*
        Move down
        */

        /*
        strafe left
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT));
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                    if(
                        (groundTree.getState()==MovementTreeState.IDLE || groundTree.getState()==MovementTreeState.SLOWDOWN) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        groundTree.start(MovementRelativeFacing.LEFT);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                    if(
                        (groundTree.getState()==MovementTreeState.IDLE || groundTree.getState()==MovementTreeState.SLOWDOWN) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        groundTree.start(MovementRelativeFacing.LEFT);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_LEFT).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});
        /*
        strafe right
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT));
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                    if(
                        (groundTree.getState()==MovementTreeState.IDLE || groundTree.getState()==MovementTreeState.SLOWDOWN) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        groundTree.start(MovementRelativeFacing.RIGHT);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    CreatureUtils.setFacingVector(Globals.playerEntity, CameraEntityUtils.getFacingVec(Globals.playerCamera));
                    if(
                        (groundTree.getState()==MovementTreeState.IDLE || groundTree.getState()==MovementTreeState.SLOWDOWN) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_FORWARD).getKeyValue())) &&
                        (controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).isIsKey() && !Globals.controlCallback.getKey(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_BACKWARD).getKeyValue()))
                    ){
                        groundTree.start(MovementRelativeFacing.RIGHT);
                    }
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_STRAFE_RIGHT).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                BehaviorTree movementTree = CreatureUtils.clientGetEntityMovementTree(Globals.playerEntity);
                if(movementTree instanceof ClientGroundMovementTree){
                    ClientGroundMovementTree groundTree = (ClientGroundMovementTree) movementTree;
                    groundTree.slowdown();
                }
            }
        }});
        /*
        Jump
        DATA_STRING_INPUT_CODE_MOVEMENT_JUMP
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_JUMP));
        controls.get(DATA_STRING_INPUT_CODE_MOVEMENT_JUMP).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                ClientJumpTree jumpTree = ClientJumpTree.getClientJumpTree(Globals.playerEntity);
                if(jumpTree != null){
                    jumpTree.start();
                }
            }
        }});
        /*
        Sprint
        */
        mainGameControlList.add(controls.get(INPUT_CODE_SPRINT));
        controls.get(INPUT_CODE_SPRINT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                SprintTree sprintTree = CreatureUtils.clientGetSprintTree(Globals.playerEntity);
                if(sprintTree != null){
                    sprintTree.start();
                }
            }
        }});
        controls.get(INPUT_CODE_SPRINT).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                SprintTree sprintTree = CreatureUtils.clientGetSprintTree(Globals.playerEntity);
                if(sprintTree != null){
                    sprintTree.stop();
                }
            }
        }});

        mainGameControlList.add(controls.get(ITEM_SECONDARY));
        controls.get(ITEM_SECONDARY).setOnPress(new ControlMethod() {public void execute() {
            ItemActions.attemptSecondaryItemAction();
        }});
        controls.get(ITEM_SECONDARY).setOnRelease(new ControlMethod() {public void execute() {
            ItemActions.releaseSecondaryItemAction();
        }});
        
        /*
        Interact
        */
        mainGameControlList.add(controls.get(INPUT_CODE_INTERACT));
        controls.get(INPUT_CODE_INTERACT).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                if(ClientEquipState.hasEquipState(Globals.playerEntity) && Crosshair.hasTarget()){
                    if(InventoryUtils.hasNaturalInventory(Globals.playerEntity)){
                        InventoryUtils.clientAttemptStoreItem(Globals.playerEntity, Crosshair.getTarget());
                    }
                }
            }
        }});
        
        /*
        Drop
        */
        mainGameControlList.add(controls.get(INPUT_CODE_DROP));
        controls.get(INPUT_CODE_DROP).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                if(ClientEquipState.hasEquipState(Globals.playerEntity)){
                    UnrelationalInventoryState inventory = InventoryUtils.getNaturalInventory(Globals.playerEntity);
                    if(inventory.getItems().size() > 0){
                        Entity itemToDrop = inventory.getItems().get(0);
                        InventoryUtils.clientAttemptEjectItem(Globals.playerEntity,itemToDrop);
                    }
                }
            }
        }});
        
        /*
        Attack
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY));
        controls.get(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY).setOnPress(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                // Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                ClientAttackTree attackTree = CreatureUtils.clientGetAttackTree(Globals.playerEntity);
                if(attackTree != null){
                    // CreatureUtils.setFacingVector(Globals.playerCharacter, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).normalize());
                    attackTree.start();
                }
                ShooterTree shooterTree = ShooterTree.getShooterTree(Globals.playerEntity);
                if(shooterTree != null){
                    shooterTree.fire();
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY).setOnRepeat(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                ShooterTree shooterTree = ShooterTree.getShooterTree(Globals.playerEntity);
                if(shooterTree != null){
                    shooterTree.fire();
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY).setOnRelease(new ControlMethod(){public void execute(){
            if(Globals.playerEntity != null){
                // Vector3f cameraEyeVector = CameraEntityUtils.getCameraEye(Globals.playerCamera);
                ClientAttackTree attackTree = CreatureUtils.clientGetAttackTree(Globals.playerEntity);
                if(attackTree != null){
                    // CreatureUtils.setFacingVector(Globals.playerCharacter, new Vector3d(-cameraEyeVector.x,0,-cameraEyeVector.z).normalize());
                    attackTree.release();
                }
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_ATTACK_PRIMARY).setRepeatTimeout(0.5f * Main.targetFrameRate);
        
        
        /*
        Lock on crosshair
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_LOCK_CROSSHAIR));
        controls.get(DATA_STRING_INPUT_CODE_LOCK_CROSSHAIR).setOnPress(new ControlMethod(){public void execute(){
            if(Crosshair.hasTarget()){
                Crosshair.setCrosshairActive(true);
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_LOCK_CROSSHAIR).setOnRelease(new ControlMethod(){public void execute(){
            if(Crosshair.getCrosshairActive()){
                Crosshair.setCrosshairActive(false);
            }
        }});
        
        
        /*
        Main menu dialog toggle
        */
        mainGameControlList.add(controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU));
        controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU).setOnClick(new ControlMethod(){public void execute(){
            // Globals.elementManager.registerWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, MenuGenerators.createInGameMainMenu());
            // Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU);

            // Window mainMenuWindow = new Window(0, 0, Globals.WINDOW_WIDTH, Globals.WINDOW_HEIGHT);
            Window mainMenuInGame = MenuGeneratorsInGame.createInGameMainMenu();
            // mainMenuWindow.addChild(mainMenuInGame);
            Globals.elementManager.registerWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN, mainMenuInGame);
            WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN), true);
            Globals.elementManager.focusFirstElement();
            Globals.controlHandler.hintUpdateControlState(ControlsState.IN_GAME_MAIN_MENU);
            //play sound effect
            if(Globals.virtualAudioSourceManager != null){
                Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false);
            }
        }});
        controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU).setRepeatTimeout(0.5f * Main.targetFrameRate);

        /*
        Open inventory
        */
        mainGameControlList.add(controls.get(INPUT_CODE_INVENTORY_OPEN));
        inventoryControlList.add(controls.get(INPUT_CODE_INVENTORY_OPEN));
        controls.get(INPUT_CODE_INVENTORY_OPEN).setOnClick(new ControlMethod(){public void execute(){
            if(InventoryUtils.hasNaturalInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(InventoryUtils.getNaturalInventory(Globals.playerEntity).getId())) == null){
                //create window
                UnrelationalInventoryState inventory = InventoryUtils.getNaturalInventory(Globals.playerEntity);
                Window mainMenuWindow = MenuGeneratorsInventory.createNaturalInventoryMenu(inventory);
                //register
                Globals.elementManager.registerWindow(WindowUtils.getInventoryWindowID(inventory.getId()), mainMenuWindow);
                //make visible
                WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(inventory.getId())), true);
                //controls
                Globals.controlHandler.hintUpdateControlState(ControlsState.INVENTORY);
                //play sound effect
                if(Globals.virtualAudioSourceManager != null){
                    Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false);
                }
                //
                Globals.openInventoriesCount++;
            } else if(InventoryUtils.hasNaturalInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(InventoryUtils.getNaturalInventory(Globals.playerEntity).getId())) != null){
                Globals.elementManager.closeWindow(WindowUtils.getInventoryWindowID(InventoryUtils.getNaturalInventory(Globals.playerEntity).getId()));
            }
        }});
        controls.get(INPUT_CODE_INVENTORY_OPEN).setRepeatTimeout(0.5f * Main.targetFrameRate);

        /*
        Open character
        */
        mainGameControlList.add(controls.get(INPUT_CODE_CHARACTER_OPEN));
        inventoryControlList.add(controls.get(INPUT_CODE_CHARACTER_OPEN));
        controls.get(INPUT_CODE_CHARACTER_OPEN).setOnClick(new ControlMethod(){public void execute(){
            if(InventoryUtils.hasEquipInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER) == null){
                //create window
                Window characterInventoryMenu = MenuGeneratorsInventory.createCharacterInventoryMenu(InventoryUtils.getEquipInventory(Globals.playerEntity));
                //register
                Globals.elementManager.registerWindow(WindowStrings.WINDOW_CHARACTER, characterInventoryMenu);
                //make visible
                WindowUtils.recursiveSetVisible(Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER), true);
                //controls
                Globals.controlHandler.hintUpdateControlState(ControlsState.INVENTORY);
                //play sound effect
                if(Globals.virtualAudioSourceManager != null){
                    Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false);
                }
                //
                Globals.openInventoriesCount++;
            } else if(InventoryUtils.hasEquipInventory(Globals.playerEntity) && Globals.elementManager.getWindow(WindowStrings.WINDOW_CHARACTER) != null){
                Globals.elementManager.closeWindow(WindowStrings.WINDOW_CHARACTER);
            }
        }});
        controls.get(INPUT_CODE_CHARACTER_OPEN).setRepeatTimeout(0.5f * Main.targetFrameRate);

        mainGameControlList.add(controls.get(INPUT_CODE_PLACE_TERRAIN));
        controls.get(INPUT_CODE_PLACE_TERRAIN).setOnPress(new ControlMethod(){public void execute(){
            CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
            Entity camera = Globals.playerCamera;
            if(
                collisionEngine != null &&
                camera != null
            ){
                Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
                Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
                Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
                if(Globals.clientSelectedVoxelType != null){
                    TerrainEditing.editTerrain(cursorPos, 1.1f, Globals.clientSelectedVoxelType.getId(), 0.1f);
                }
            }
        }});
        controls.get(INPUT_CODE_PLACE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){
            CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
            Entity camera = Globals.playerCamera;
            if(
                collisionEngine != null &&
                camera != null
            ){
                Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
                Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
                Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
                if(Globals.clientSelectedVoxelType != null){
                    TerrainEditing.editTerrain(cursorPos, 1.1f, Globals.clientSelectedVoxelType.getId(), 0.1f);
                }
            }
        }});
        controls.get(INPUT_CODE_PLACE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate);

        mainGameControlList.add(controls.get(INPUT_CODE_REMOVE_TERRAIN));
        controls.get(INPUT_CODE_REMOVE_TERRAIN).setOnPress(new ControlMethod(){public void execute(){
            CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
            Entity camera = Globals.playerCamera;
            if(
                collisionEngine != null &&
                camera != null
            ){
                Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
                Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
                Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
                TerrainEditing.editTerrain(cursorPos, 1.1f, 1, -0.01f);
            }
        }});
        controls.get(INPUT_CODE_REMOVE_TERRAIN).setOnRepeat(new ControlMethod(){public void execute(){
            CollisionEngine collisionEngine = Globals.clientSceneWrapper.getCollisionEngine();
            Entity camera = Globals.playerCamera;
            if(
                collisionEngine != null &&
                camera != null
            ){
                Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera));
                Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera));
                Vector3d cursorPos = collisionEngine.rayCastPosition(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1.0), 5.0);
                TerrainEditing.editTerrain(cursorPos, 1.1f, 1, -0.01f);
            }
        }});
        controls.get(INPUT_CODE_REMOVE_TERRAIN).setRepeatTimeout(0.2f * Main.targetFrameRate);

    }
    

    /**
     * Sets the in game debug control callbacks
     */
    void setInGameDebugControls(){
        mainGameDebugControlList.add(controls.get(DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM));
        controls.get(DATA_STRING_INPUT_CODE_DEBUG_SPAWN_ITEM).setOnPress(new ControlMethod(){public void execute(){
            // RenderingEngine.incrementOutputFramebuffer();
            // Entity bow = ItemUtils.spawnBasicItem("shorts1");
            // EntityUtils.getPosition(bow).set(1, 5, 2);
            // CollisionObjUtils.positionCharacter(bow, new Vector3f(1, 5, 2));
        }});

        mainGameDebugControlList.add(controls.get(DEBUG_FRAMESTEP));
        controls.get(DEBUG_FRAMESTEP).setOnRelease(new ControlMethod(){public void execute(){
            Main.setFramestep(1);
        }});
        controls.get(DEBUG_FRAMESTEP).setOnRepeat(new ControlMethod(){public void execute(){
            Main.setFramestep(1);
        }});
        controls.get(DEBUG_FRAMESTEP).setRepeatTimeout(0.5f * Main.targetFrameRate);
        // RenderingEngine.incrementOutputFramebuffer();
    }

    void setAlwaysOnDebugControls(){
        alwaysOnDebugControlList.add(controls.get(DEBUG_OPEN_DEBUG_MENU));
        controls.get(DEBUG_OPEN_DEBUG_MENU).setOnPress(new ControlMethod(){public void execute(){
            LoggerInterface.loggerEngine.INFO("open debug menu");
            ImGuiWindowMacros.toggleMainDebugMenu();
            //play sound effect
            if(Globals.virtualAudioSourceManager != null){
                Globals.virtualAudioSourceManager.createVirtualAudioSource("/Audio/openMenu.ogg", VirtualAudioSourceType.UI, false);
            }
        }});
        controls.get(DEBUG_OPEN_DEBUG_MENU).setRepeatTimeout(0.5f * Main.targetFrameRate);
    }
    
    void setMenuNavigationControls(){
        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_FORWARD));
        controls.get(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_FORWARD).setOnPress(new ControlMethod(){public void execute(){
            // Globals.currentMenu.incrementMenuOption();
            Globals.elementManager.focusNextElement();
        }});

        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_BACKWARDS));
        controls.get(DATA_STRING_INPUT_CODE_MENU_NAVIGATE_BACKWARDS).setOnPress(new ControlMethod(){public void execute(){
            // Globals.currentMenu.decrementMenuOption();
            Globals.elementManager.focusPreviousElement();
        }});



        //Incrementing a menu element
        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_INCREMENT));
        controls.get(DATA_STRING_INPUT_CODE_MENU_INCREMENT).setOnPress(new ControlMethod(){public void execute(){
            Globals.elementManager.fireEvent(
                new MenuEvent(MenuEventType.INCREMENT),
                Globals.elementManager.getFocusedElement().getAbsoluteX(),
                Globals.elementManager.getFocusedElement().getAbsoluteY()
            );
        }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_INCREMENT).setOnRepeat(new ControlMethod(){public void execute(){
            Globals.elementManager.fireEvent(
                new MenuEvent(MenuEventType.INCREMENT),
                Globals.elementManager.getFocusedElement().getAbsoluteX(),
                Globals.elementManager.getFocusedElement().getAbsoluteY()
            );
        }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_INCREMENT).setRepeatTimeout(0.5f * Main.targetFrameRate);

        //moving the mouse
        menuNavigationControlList.add(controls.get(MENU_MOUSE_MOVE));
        controls.get(MENU_MOUSE_MOVE).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent mouseEvent){
            Globals.elementManager.updateHover(mouseEvent.getCurrentX(), mouseEvent.getCurrentY());
        }});

        //scrolling the mouse
        menuNavigationControlList.add(controls.get(MENU_SCROLL));
        controls.get(MENU_SCROLL).setOnScroll(new Control.ScrollCallback() {public void execute(ScrollEvent scrollEvent){
            Globals.elementManager.fireEvent(scrollEvent, GLFW_KEY_X, GLFW_KEY_Z);
        }});

        //dragging the cursor
        menuNavigationControlList.add(controls.get(MENU_DRAG_MANIPULATE));
        controls.get(MENU_DRAG_MANIPULATE).setOnPress(new ControlMethod(){public void execute(){
        }});
        controls.get(MENU_DRAG_MANIPULATE).setOnRelease(new ControlMethod(){public void execute(){
            if(dragging){
                dragging = false;
                //fire dragrelease event to elementmanager
                Globals.elementManager.dragRelease((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            }
        }});
        /*
        item dragging
        */
        menuNavigationControlList.add(controls.get(MENU_DRAG_START));
        controls.get(MENU_DRAG_START).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent event){
            if(!dragging && event.getButton1()){
                dragging = true;
                //fire dragstart event to elementmanager
                Globals.elementManager.dragStart((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            }
            if(dragging){
                //fire drag event to elementmanager
                Globals.elementManager.drag((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            }
        }});
        
        /**
        Screenshots
        */
        menuNavigationControlList.add(controls.get(MENU_CAPTURE_SCREEN));
        controls.get(MENU_CAPTURE_SCREEN).setOnPress(new Control.ControlMethod() {public void execute(){
            FileUtils.writeBufferedImage(
                RenderingEngine.defaultFramebuffer.getPixels(Globals.renderingEngine.getOpenGLState()),
                "Screenshots/" + System.currentTimeMillis() + ".png"
            );
        }});
        



        //Decrementing a menu element
        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_DECREMENT));
        controls.get(DATA_STRING_INPUT_CODE_MENU_DECREMENT).setOnPress(new ControlMethod(){public void execute(){
            Globals.elementManager.fireEvent(
                new MenuEvent(MenuEventType.DECREMENT),
                Globals.elementManager.getFocusedElement().getAbsoluteX(),
                Globals.elementManager.getFocusedElement().getAbsoluteY()
            );
        }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_DECREMENT).setOnRepeat(new ControlMethod(){public void execute(){
            Globals.elementManager.fireEvent(
                new MenuEvent(MenuEventType.DECREMENT),
                Globals.elementManager.getFocusedElement().getAbsoluteX(),
                Globals.elementManager.getFocusedElement().getAbsoluteY()
            );
        }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_DECREMENT).setRepeatTimeout(0.5f * Main.targetFrameRate);




        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_SELECT));
        controls.get(DATA_STRING_INPUT_CODE_MENU_SELECT).setOnPress(new ControlMethod(){public void execute(){
            Globals.elementManager.click(new ClickEvent(
                Globals.elementManager.getFocusedElement().getInternalX() + 1,
                Globals.elementManager.getFocusedElement().getInternalY() + 1,
                true,
                Globals.mouseCallback.getButton(GLFW_MOUSE_BUTTON_2)
            ));
            // MenuCallbacks.selectOption(Globals.currentMenu);
        }});

        menuNavigationControlList.add(controls.get(INPUT_CODE_MENU_MOUSE_PRIMARY));
        mainGameDebugControlList.add(controls.get(INPUT_CODE_MENU_MOUSE_PRIMARY));
        controls.get(INPUT_CODE_MENU_MOUSE_PRIMARY).setOnClick(new ControlMethod(){public void execute(){
            Globals.elementManager.click(new ClickEvent(
                (int)xpos,
                (int)ypos,
                true,
                Globals.mouseCallback.getButton(GLFW_MOUSE_BUTTON_2)
            ));
        }});
        controls.get(INPUT_CODE_MENU_MOUSE_PRIMARY).setRepeatTimeout(0.5f * Main.targetFrameRate);

        menuNavigationControlList.add(controls.get(DATA_STRING_INPUT_CODE_MENU_BACKOUT));
        // controls.get(DATA_STRING_INPUT_CODE_MENU_BACKOUT).setOnPress(new ControlMethod(){public void execute(){
        //     // MenuCallbacks.backout(Globals.currentMenu);
        //     Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN);
        // }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_BACKOUT).setOnClick(new ControlMethod(){public void execute(){
            // MenuCallbacks.backout(Globals.currentMenu);
            Globals.elementManager.navigateBackwards();
            // Globals.elementManager.unregisterWindow(WindowStrings.WINDOW_MENU_INGAME_MAIN);
        }});
        controls.get(DATA_STRING_INPUT_CODE_MENU_BACKOUT).setRepeatTimeout(0.5f * Main.targetFrameRate);
        
    }

    /**
     * Sets the freecam control callbacks
     */
    void setFreecamControls(){
        freeCameraControlList.add(controls.get(FREECAM_UP));
        controls.get(FREECAM_UP).setOnRepeat(new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            playerCameraCenterPos.add(0,0.1f,0);
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }});


        freeCameraControlList.add(controls.get(FREECAM_DOWN));
        controls.get(FREECAM_DOWN).setOnRepeat(new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            playerCameraCenterPos.add(0,-0.1f,0);
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }});


        freeCameraControlList.add(controls.get(FREECAM_FORWARD));
        ControlMethod freeCamForwardCallback = new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
            playerCameraCenterPos.add(new Vector3f(playerCameraEyePos).normalize().mul(-0.1f));
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }};
        controls.get(FREECAM_FORWARD).setOnClick(freeCamForwardCallback);
        controls.get(FREECAM_FORWARD).setOnRepeat(freeCamForwardCallback);

        freeCameraControlList.add(controls.get(FREECAM_BACKWARD));
        ControlMethod freeCamBackwardCallback = new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
            playerCameraCenterPos.add(new Vector3f(playerCameraEyePos).normalize().mul(0.1f));
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }};
        controls.get(FREECAM_BACKWARD).setOnClick(freeCamBackwardCallback);
        controls.get(FREECAM_BACKWARD).setOnRepeat(freeCamBackwardCallback);

        freeCameraControlList.add(controls.get(FREECAM_LEFT));
        ControlMethod freeCamLeftCallback = new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
            Vector3f modifiedVec = new Vector3f(playerCameraEyePos.x,0,playerCameraEyePos.z).rotateY((float)(-90 * Math.PI / 180)).normalize();
            playerCameraCenterPos.add(new Vector3f(modifiedVec).mul(0.1f));
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }};
        controls.get(FREECAM_LEFT).setOnClick(freeCamLeftCallback);
        controls.get(FREECAM_LEFT).setOnRepeat(freeCamLeftCallback);

        freeCameraControlList.add(controls.get(FREECAM_RIGHT));
        ControlMethod freeCamRightCallback = new ControlMethod(){public void execute(){
            Vector3f playerCameraCenterPos = CameraEntityUtils.getCameraCenter(Globals.playerCamera);
            Vector3f playerCameraEyePos = CameraEntityUtils.getCameraEye(Globals.playerCamera);
            Vector3f modifiedVec = new Vector3f(playerCameraEyePos.x,0,playerCameraEyePos.z).rotateY((float)(90 * Math.PI / 180)).normalize();
            playerCameraCenterPos.add(new Vector3f(modifiedVec).mul(0.1f));
            CameraEntityUtils.setCameraCenter(Globals.playerCamera,playerCameraCenterPos);
        }};
        controls.get(FREECAM_RIGHT).setOnClick(freeCamRightCallback);
        controls.get(FREECAM_RIGHT).setOnRepeat(freeCamRightCallback);

        //terrain controls -- these should have already been defined in the main game controls section
        freeCameraControlList.add(controls.get(INPUT_CODE_PLACE_TERRAIN));
        freeCameraControlList.add(controls.get(INPUT_CODE_REMOVE_TERRAIN));

        freeCameraControlList.add(controls.get(FREECAM_MOUSE));
        controls.get(FREECAM_MOUSE).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent event){
            Globals.cameraHandler.handleMouseEvent(event);
        }});

        freeCameraControlList.add(controls.get(DATA_STRING_INPUT_CODE_IN_GAME_MAIN_MENU));
        
    }
    
    /**
     * Sets the typing control callbacks
     */
    void setTypingControls(){
        
        String[] typeKeybinds = {
            DATA_STRING_INPUT_CODE_MENU_TYPE_BACKSPACE,
            DATA_STRING_INPUT_CODE_MENU_TYPE_0,
            DATA_STRING_INPUT_CODE_MENU_TYPE_1,
            DATA_STRING_INPUT_CODE_MENU_TYPE_2,
            DATA_STRING_INPUT_CODE_MENU_TYPE_3,
            DATA_STRING_INPUT_CODE_MENU_TYPE_4,
            DATA_STRING_INPUT_CODE_MENU_TYPE_5,
            DATA_STRING_INPUT_CODE_MENU_TYPE_6,
            DATA_STRING_INPUT_CODE_MENU_TYPE_7,
            DATA_STRING_INPUT_CODE_MENU_TYPE_8,
            DATA_STRING_INPUT_CODE_MENU_TYPE_9,
            DATA_STRING_INPUT_CODE_MENU_TYPE_PERIOD,
            DATA_STRING_INPUT_CODE_MENU_TYPE_A,
            DATA_STRING_INPUT_CODE_MENU_TYPE_B,
            DATA_STRING_INPUT_CODE_MENU_TYPE_C,
            DATA_STRING_INPUT_CODE_MENU_TYPE_D,
            DATA_STRING_INPUT_CODE_MENU_TYPE_E,
            DATA_STRING_INPUT_CODE_MENU_TYPE_F,
            DATA_STRING_INPUT_CODE_MENU_TYPE_G,
            DATA_STRING_INPUT_CODE_MENU_TYPE_H,
            DATA_STRING_INPUT_CODE_MENU_TYPE_I,
            DATA_STRING_INPUT_CODE_MENU_TYPE_J,
            DATA_STRING_INPUT_CODE_MENU_TYPE_K,
            DATA_STRING_INPUT_CODE_MENU_TYPE_L,
            DATA_STRING_INPUT_CODE_MENU_TYPE_M,
            DATA_STRING_INPUT_CODE_MENU_TYPE_N,
            DATA_STRING_INPUT_CODE_MENU_TYPE_O,
            DATA_STRING_INPUT_CODE_MENU_TYPE_P,
            DATA_STRING_INPUT_CODE_MENU_TYPE_Q,
            DATA_STRING_INPUT_CODE_MENU_TYPE_R,
            DATA_STRING_INPUT_CODE_MENU_TYPE_S,
            DATA_STRING_INPUT_CODE_MENU_TYPE_T,
            DATA_STRING_INPUT_CODE_MENU_TYPE_U,
            DATA_STRING_INPUT_CODE_MENU_TYPE_V,
            DATA_STRING_INPUT_CODE_MENU_TYPE_W,
            DATA_STRING_INPUT_CODE_MENU_TYPE_X,
            DATA_STRING_INPUT_CODE_MENU_TYPE_Y,
            DATA_STRING_INPUT_CODE_MENU_TYPE_Z,
            INPUT_CODE_MENU_TYPE_FORWARD_SLASH,
        };
        for(String currentKey : typeKeybinds){
            typingControlList.add(controls.get(currentKey));
            controls.get(currentKey).setOnPress(new ControlMethod(){public void execute(){
                Globals.elementManager.fireEventNoPosition(
                    new KeyboardEvent(convertKeycodeToName(controls.get(currentKey).keyValue)),
                    Globals.elementManager.getFocusedElement()
                );
                // MenuCallbacks.menuHandleKeypress(Globals.currentMenu,currentKey);
            }});
            // if(controls.get(currentKey).isIsKey() && Globals.controlCallback.getKey(controls.get(currentKey).getKeyValue())){
            //     controls.get(currentKey).setState(true);
            // } else {
            //     if(controls.get(currentKey).isState() == true){
            //         MenuTransition.menuHandleKeypress(Globals.currentMenu,currentKey);
            //     }
            //     controls.get(currentKey).setState(false);
            // }
        }
    }

    /**
     * Sets the inventory control callbacks
     */
    void setInventoryControls(){
        /*
        Close inventory
        */
        // inventoryControlList.add(controls.get(INPUT_CODE_INVENTORY_CLOSE));
        // controls.get(INPUT_CODE_INVENTORY_CLOSE).setOnPress(new ControlMethod(){public void execute(){
        //     // MenuCallbacks.backout(Globals.currentMenu);
        //     UnrelationalInventoryState  inventory = InventoryUtils.getNaturalInventory(Globals.playerCharacter);
        //     Element inventoryWindow = Globals.elementManager.getWindow(WindowUtils.getInventoryWindowID(inventory.getId()));
        //     WindowUtils.recursiveSetVisible(inventoryWindow, false);
        //     hideMouse();
        //     Globals.elementManager.unregisterWindow(WindowUtils.getInventoryWindowID(inventory.getId()));
        //     Globals.controlHandler.setHandlerState(ControlsState.MAIN_GAME);
        // }});
        /*
        Item manipulation
        */
        inventoryControlList.add(controls.get(INPUT_CODE_INVENTORY_ITEM_MANIPULATE));
        controls.get(INPUT_CODE_INVENTORY_ITEM_MANIPULATE).setOnPress(new ControlMethod(){public void execute(){
        }});
        controls.get(INPUT_CODE_INVENTORY_ITEM_MANIPULATE).setOnRelease(new ControlMethod(){public void execute(){
            if(dragging){
                dragging = false;
                //fire dragrelease event to elementmanager
                Globals.elementManager.dragRelease((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            } else {
                //fire onclick event to elementmanager
                Globals.elementManager.click(new ClickEvent((int)xpos,(int)ypos,true,Globals.mouseCallback.getButton(GLFW_MOUSE_BUTTON_2)));
            }
        }});
        /*
        item dragging
        */
        inventoryControlList.add(controls.get(INPUT_CODE_INVENTORY_ITEM_DRAG));
        controls.get(INPUT_CODE_INVENTORY_ITEM_DRAG).setOnMove(new Control.MouseCallback(){public void execute(MouseEvent event){
            if(!dragging && event.getButton1()){
                dragging = true;
                //fire dragstart event to elementmanager
                Globals.elementManager.dragStart((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            }
            if(dragging){
                //fire drag event to elementmanager
                Globals.elementManager.drag((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)(xpos - mouse_lastX),(int)(mouse_lastY - ypos));
            }
        }});
    }

    /**
     * Gets the main controls list
     * @return The main controls
     */
    public List<Control> getMainControlsList(){
        return mainGameControlList;
    }


    /**
     * Checks a list of controls to see if the corresponding key/mouse event is firing this frame
     * @param controls The list of controls to check
     */
    public void runHandlers(List<Control> controls){

        //construct mouse event

        //Fills the buffer
        getMousePositionInBuffer();
        float xoffset = (float) (xpos - mouse_lastX);
        float yoffset = (float) (mouse_lastY - ypos);
        mouse_lastX = (float) xpos;
        mouse_lastY = (float) ypos;
        MouseEvent currentMouseEvent = new MouseEvent((int)xpos,(int)ypos,(int)mouse_lastX,(int)mouse_lastY,(int)xoffset,(int)yoffset,getButton1Raw(),getButton2Raw());
        boolean mouseMoveEvent = xoffset != 0 || yoffset != 0;


        for(Control control : controls){
            switch(control.getType()){
                case KEY: {
                    if(Globals.controlCallback.getKey(control.getKeyValue())){
                        if(!control.isState()){
                            //on press
                            control.onPress();
                            control.setPressFrame((float)Globals.timekeeper.getNumberOfRenderFramesElapsed());
                        } else {
                            //on repeat
                            if((float)Globals.timekeeper.getNumberOfRenderFramesElapsed() - control.getPressFrame() > control.getRepeatTimeout()){
                                control.onRepeat();
                            }
                        }
                        control.setState(true);
                    } else {
                        if(control.isState()){
                            //on release
                            control.onRelease();
                            //on click
                            if((float)Globals.timekeeper.getNumberOfRenderFramesElapsed() - control.getPressFrame() < control.getRepeatTimeout()){
                                control.onClick();
                            }
                        } else {
                        }
                        control.setState(false);
                    }
                } break;
                case MOUSE_BUTTON: {
                    if(Globals.mouseCallback.getButton(control.getKeyValue())){
                        if(!control.isState()){
                            //on press
                            control.onPress();
                            control.setPressFrame((float)Globals.timekeeper.getNumberOfRenderFramesElapsed());
                        } else {
                            //on repeat
                            control.onRepeat();
                        }
                        control.setState(true);
                    } else {
                        if(control.isState()){
                            //on release
                            control.onRelease();
                            if((float)Globals.timekeeper.getNumberOfRenderFramesElapsed() - control.getPressFrame() < control.getRepeatTimeout()){
                                control.onClick();
                            }
                        } else {
                        }
                        control.setState(false);
                    }
                } break;
                case MOUSE_MOVEMENT: {
                    if(mouseMoveEvent){
                        control.onMove(currentMouseEvent);
                    }
                } break;
                case MOUSE_SCROLL: {
                    double yScroll = Globals.scrollCallback.getOffsetY();
                    if(yScroll != 0){
                        ScrollEvent event = new ScrollEvent(xpos,ypos,yScroll);
                        control.onScroll(event);
                    }
                } break;
            }
        }
    }

    /**
     * Checks if any menus are open that would intercept player input (main menu, inventory, debug, etc)
     * @return true if such a menu is open, false otherwise
     */
    private boolean hasControlBlockingMenuOpen(){
        boolean rVal = false;
        //check main ui framework windows
        for(String windowString : controlBlockingWindows){
            rVal = rVal || WindowUtils.windowIsOpen(windowString);
        }
        //check imgui windows
        rVal = rVal || Globals.renderingEngine.getImGuiPipeline().shouldCaptureControls();
        return rVal;
    }

    /**
     * Hints to the engine that it should update the control state
     * The provided control state will be overwritten if, for instance,
     * there is a menu open that demands mouse input and you are trying
     * to tell the engine to convert to immediate player control
     * @param desiredState The desired control state
     */
    public void hintUpdateControlState(ControlsState desiredState){
        ControlsState properState = desiredState;
        //correct for freecam or actual ingame control based on value of getTrackPlayerEntity
        if(desiredState == ControlsState.IN_GAME_FREE_CAMERA && Globals.cameraHandler.getTrackPlayerEntity()){
            properState = ControlsState.MAIN_GAME;
        }
        if(desiredState == ControlsState.MAIN_GAME && !Globals.cameraHandler.getTrackPlayerEntity()){
            properState = ControlsState.IN_GAME_FREE_CAMERA;
        }

        //set to menu state if a menu is open, otherwise use the hinted control scheme
        if(hasControlBlockingMenuOpen()){
            setHandlerState(ControlsState.IN_GAME_MAIN_MENU);
        } else {
            setHandlerState(properState);
        }
        //checks if the current handler state should have mouse enabled or not
        if(Arrays.binarySearch(mouseEnabledStates,getHandlerState()) >= 0){
            showMouse();
        } else {
            hideMouse();
        }
    }

    /**
     * Transfers the mouse position from the glfw buffer to variables stored inside the control handler
     */
    void getMousePositionInBuffer(){
        //only if not headless, gather position
        if(!Globals.HEADLESS){
            glfwGetCursorPos(Globals.window, mouse_X_Buffer, mouse_Y_Buffer);
            xpos = mouse_X_Buffer[0];
            ypos = mouse_Y_Buffer[0];
        }
    }

    /**
     * Checks if the mouse button 1 is currently pressed
     * @return true if pressed, false otherwise
     */
    boolean getButton1Raw(){
        if(Globals.HEADLESS){
            return false;
        } else {
            return Globals.mouseCallback.getButton(GLFW_MOUSE_BUTTON_1);
        }
    }

    /**
     * Checks if the mouse button 2 is currently pressed
     * @return true if pressed, false otherwise
     */
    boolean getButton2Raw(){
        if(Globals.HEADLESS){
            return false;
        } else {
            return Globals.mouseCallback.getButton(GLFW_MOUSE_BUTTON_2);
        }
    }
    
    public Control getControl(String controlName){
        return controls.get(controlName);
    }
    
    public boolean containsControl(String controlName){
        return controls.containsKey(controlName);
    }
    
    public void removeControl(String controlName){
        controls.remove(controlName);
    }
    
    public void addControl(String controlName, Control c){
        controls.put(controlName, c);
    }
    
    /**
     * Sets the state of the controls handler
     * @param state the state
     */
    private void setHandlerState(ControlsState state){
        this.state = state;
    }

    /**
     * Gets the current state of the controls handler
     * @return the state
     */
    public ControlsState getHandlerState(){
        return state;
    }
    
    /**
     * Hides the mouse
     */
    public void hideMouse(){
        glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
        mouseIsVisible = false;
    }
    
    /**
     * Shows the mouse
     */
    public void showMouse(){
        glfwSetInputMode(Globals.window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        mouseIsVisible = true;
    }
    
    /**
     * Gets whether the mouse is visible or not
     * @return true if visible, false otherwise
     */
    public boolean isMouseVisible(){
        return mouseIsVisible;
    }
    
    /**
     * Gets the mouse position as a vector2f
     * @return The vector containing the mouse position
     */
    public Vector2f getMousePosition(){
        double posX[] = new double[1];
        double posY[] = new double[1];
        glfwGetCursorPos(Globals.window, posX, posY);
        Vector2f rVal = new Vector2f((float)posX[0],(float)posY[0]);
        return rVal;
    }
    
    /**
     * Converts a keycode to a string containing a code related to the keycode (ie "A" for 65, "Escape" for 256, etc)
     * @param code The keycode
     * @return The corresponding string code
     */
    public static String convertKeycodeToName(int code){
        String rVal = "";
        switch(code){
            case 46:
            rVal = ".";
            break;
            case 47:
            rVal = "/";
            break;
            case 48:
                rVal = "0";
                break;
            case 49:
                rVal = "1";
                break;
            case 50:
                rVal = "2";
                break;
            case 51:
                rVal = "3";
                break;
            case 52:
                rVal = "4";
                break;
            case 53:
                rVal = "5";
                break;
            case 54:
                rVal = "6";
                break;
            case 55:
                rVal = "7";
                break;
            case 56:
                rVal = "8";
                break;
            case 57:
                rVal = "9";
                break;
            case 65:
                rVal = "A";
                break;
            case 66:
                rVal = "B";
                break;
            case 67:
                rVal = "C";
                break;
            case 68:
                rVal = "D";
                break;
            case 69:
                rVal = "E";
                break;
            case 70:
                rVal = "F";
                break;
            case 71:
                rVal = "G";
                break;
            case 72:
                rVal = "H";
                break;
            case 73:
                rVal = "I";
                break;
            case 74:
                rVal = "J";
                break;
            case 75:
                rVal = "K";
                break;
            case 76:
                rVal = "L";
                break;
            case 77:
                rVal = "M";
                break;
            case 78:
                rVal = "N";
                break;
            case 79:
                rVal = "O";
                break;
            case 80:
                rVal = "P";
                break;
            case 81:
                rVal = "Q";
                break;
            case 82:
                rVal = "R";
                break;
            case 83:
                rVal = "S";
                break;
            case 84:
                rVal = "T";
                break;
            case 85:
                rVal = "U";
                break;
            case 86:
                rVal = "V";
                break;
            case 87:
                rVal = "W";
                break;
            case 88:
                rVal = "X";
                break;
            case 89:
                rVal = "Y";
                break;
            case 90:
                rVal = "Z";
                break;
            case 259:
            rVal = "bs"; //backspace
            break;
            case 256:
                rVal = "Escape";
                break;
            default:
            LoggerInterface.loggerEngine.WARNING("Unable to convert keycode " + code + " in control handler.");
            break;
        }
        return rVal;
    }
    
    /**
     * Sets whether the engine should try to recapture window focus next frame or not
     * @param shouldRecapture true if should try to recapture next frame, false otherwise
     */
    public void setRecapture(boolean shouldRecapture){
        this.shouldRecaptureScreen = shouldRecapture;
    }

    /**
     * Returns whether the engine should try to recapture window focus next frame or not
     * @return true if it should try to recapture, false otherwise
     */
    public boolean shouldRecapture(){
        return this.shouldRecaptureScreen;
    }

    /**
     * Checks if the camera is third person
     * @return true if third person, false if first person
     */
    public boolean cameraIsThirdPerson(){
        return cameraIsThirdPerson;
    }

    /**
     * Sets the 1st/3rd person status of the camera
     * @param isThirdPerson True for 3rd person, false for 1st person
     */
    public void setIsThirdPerson(boolean isThirdPerson){
        this.cameraIsThirdPerson = isThirdPerson;
        CameraEntityUtils.initCamera();
        ClientEquipState playerEquipState = ClientEquipState.getClientEquipState(Globals.playerEntity);
        if(playerEquipState != null){
            playerEquipState.evaluatePlayerAttachments();
        }
    }
    
    
}
