// Copyright 2020 The Terasology Foundation
// SPDX-License-Identifier: Apache-2.0

package org.terasology.input.lwjgl;

import com.google.common.collect.Lists;
import gnu.trove.set.TIntSet;
import gnu.trove.set.hash.TIntHashSet;
import org.joml.Vector2d;
import org.joml.Vector2i;
import org.lwjgl.BufferUtils;
import org.lwjgl.glfw.GLFW;
import org.terasology.config.Config;
import org.terasology.config.RenderingConfig;
import org.terasology.context.Context;
import org.terasology.input.ButtonState;
import org.terasology.input.InputType;
import org.terasology.input.MouseInput;
import org.terasology.input.device.MouseAction;
import org.terasology.input.device.MouseDevice;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.nio.DoubleBuffer;
import java.util.Queue;

/**
 * Lwjgl 3's (GLFW) mouse device represenation.
 * Handles mouse input via GLFW's callbacks
 * Handles mouse state.
 */
public class LwjglMouseDevice implements MouseDevice, PropertyChangeListener {
    private RenderingConfig renderingConfig;
    private float uiScale;
    private boolean mouseGrabbed;
    private Queue<MouseAction> queue = Lists.newLinkedList();

    private TIntSet buttonStates = new TIntHashSet();

    private double xpos;
    private double ypos;

    private double xposDelta;
    private double yposDelta;

    public LwjglMouseDevice(Context context) {
        this.renderingConfig = context.get(Config.class).getRendering();
        this.uiScale = this.renderingConfig.getUiScale() / 100f;
        this.renderingConfig.subscribe(RenderingConfig.UI_SCALE, this);

        // GLFW callback
        long window = GLFW.glfwGetCurrentContext();
//        GLFW.glfwSetCursorPosCallback(window, this::cursorPosCallback);
        GLFW.glfwSetMouseButtonCallback(window, this::mouseButtonCallback);
        GLFW.glfwSetScrollCallback(window, this::scrollCallback);

    }


    @Override
    public void  update() {
        long window = GLFW.glfwGetCurrentContext();
        DoubleBuffer mouseX = BufferUtils.createDoubleBuffer(1);
        DoubleBuffer mouseY = BufferUtils.createDoubleBuffer(1);

        GLFW.glfwGetCursorPos(window, mouseX, mouseY);
        mouseX.rewind();
        mouseY.rewind();

        double x = mouseX.get(0);
        double y = mouseY.get(0);

        xposDelta = x - this.xpos;
        yposDelta = y - this.ypos;
        this.xpos = x;
        this.ypos = y;
    }

    @Override
    public Vector2i getPosition() {
        return new Vector2i((int) (xpos / this.uiScale), (int)  (ypos / this.uiScale));
    }

    @Override
    public Vector2d getDelta() {

        Vector2d result = new Vector2d(xposDelta, yposDelta);
        return result;
    }

    @Override
    public boolean isButtonDown(int button) {
        return buttonStates.contains(button);
    }

    @Override
    public boolean isVisible() {
        return !mouseGrabbed;
    }

    @Override
    public void setGrabbed(boolean newGrabbed) {
        if (newGrabbed != mouseGrabbed) {
            mouseGrabbed = newGrabbed;
            GLFW.glfwSetInputMode(GLFW.glfwGetCurrentContext(), GLFW.GLFW_CURSOR,
                    newGrabbed ? GLFW.GLFW_CURSOR_DISABLED : GLFW.GLFW_CURSOR_NORMAL);
        }
    }

    @Override
    public Queue<MouseAction> getInputQueue() {
        Queue<MouseAction> mouseActions = Lists.newLinkedList();
        MouseAction action;
        while ((action = queue.poll()) != null) {
            mouseActions.add(action);
        }
        return mouseActions;
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals(RenderingConfig.UI_SCALE)) {
            this.uiScale = this.renderingConfig.getUiScale() / 100f;
        }
    }
//
//    private void cursorPosCallback(long window, double newX, double newY) {
//        xposDelta = newX - this.xpos;
//        yposDelta = newY - this.ypos;
//        this.xpos = newX;
//        this.ypos = newY;
//    }

    private void mouseButtonCallback(long window, int button, int action, int mods) {
        ButtonState state;
        if (action == GLFW.GLFW_PRESS) {
            state = ButtonState.DOWN;
            buttonStates.add(button);
        } else if (action == GLFW.GLFW_RELEASE) {
            state = ButtonState.UP;
            buttonStates.remove(button);
        } else /*if (action == GLFW.GLFW_REPEAT)*/ {
            state = ButtonState.REPEAT;
        }
        MouseInput mouseInput = MouseInput.find(InputType.MOUSE_BUTTON, button);
        queue.offer(new MouseAction(mouseInput, state, getPosition()));
    }

    private void scrollCallback(long windows, double xoffset, double yoffset) {
        if (yoffset != 0.0) {
            int id = (yoffset > 0) ? 1 : -1;
            queue.offer(new MouseAction(InputType.MOUSE_WHEEL.getInput(id), 1, getPosition()));
        }
    }
}

@@ -1686,82 +1686,8 @@
 ();%0A
-//        GLFW.glfwSetCursorPosCallback(window, this::cursorPosCallback);%0A
     
@@ -3687,233 +3687,8 @@
    %7D
-%0A//%0A//    private void cursorPosCallback(long window, double newX, double newY) %7B%0A//        xposDelta = newX - this.xpos;%0A//        yposDelta = newY - this.ypos;%0A//        this.xpos = newX;%0A//        this.ypos = newY;%0A//    %7D
 %0A%0A  

Update LwjglMouseDevice.java