package uk.co.thinkofdeath.mapviewer.client.input;

import uk.co.thinkofdeath.mapviewer.client.MapViewer;
import uk.co.thinkofdeath.mapviewer.client.render.Camera;

public class InputManager {

    private final MapViewer mapViewer;

    private int movingDirection = 0;

    /**
     * Creates a new input manager
     *
     * @param mapViewer
     *         The map viewer that owns it
     */
    public InputManager(MapViewer mapViewer) {
        this.mapViewer = mapViewer;
    }

    /**
     * Updates the input manager
     *
     * @param delta
     */
    public void update(double delta) {
        Camera camera = mapViewer.getCamera();
        if (movingDirection != 0) {
            camera.setX((float) (camera.getX() + 0.3 * Math.sin(camera.getRotationY()) *
                    Math.cos(camera.getRotationX()) * delta * movingDirection));
            camera.setZ((float) (camera.getZ() - 0.3 * Math.cos(camera.getRotationY()) *
                    Math.cos(camera.getRotationX()) * delta * movingDirection));
            camera.setY((float) (camera.getY() - 0.3 * Math.sin(camera.getRotationX()) * delta *
                    movingDirection));
        }
    }

    // This is where things get nasty.
    // Supporting multiple browsers and devices can be a pain since some browsers may use
    // prefixed events, some may use different events and others will support the event but
    // not the same variables as other browsers.
    // TODO: Touch events
    // TODO: Drag controls

    /**
     * Causes the input manager to create its hooks into the browsers events
     */
    public void hook() {
        registerNativeEvents();
    }

    private void onCanvasClick(int x, int y) {
        if (!isPointerLocked()) {
            requestPointerLock();
        }
    }

    private void onMouseLockMove(int movementX, int movementY) {
        if (!isPointerLocked()) {
            return;
        }

        Camera camera = mapViewer.getCamera();
        camera.setRotationY(camera.getRotationY() + movementX / 300f);
        camera.setRotationX(camera.getRotationX() + movementY / 300f);
        if (camera.getRotationX() > Math.PI / 2) camera.setRotationX((float) (Math.PI / 2));
        if (camera.getRotationX() < -Math.PI / 2) camera.setRotationX((float) (-Math.PI / 2));
    }

    private void onMouseMove(int x, int y) {

    }

    private boolean onKeyDown(char keyCode) {
        switch (keyCode) {
            case 'W':
                movingDirection = 1;
                return true;
            case 'S':
                movingDirection = -1;
                return true;
        }
        return false;
    }

    private boolean onKeyUp(char keyCode) {
        switch (keyCode) {
            case 'W':
                if (movingDirection == 1) {
                    movingDirection = 0;
                }
                return true;
            case 'S':
                if (movingDirection == -1) {
                    movingDirection = 0;
                }
                return true;
        }
        return false;
    }

    private native boolean isPointerLocked()/*-{
        return ($doc.pointerLockElement
            || $doc.webkitPointerLockElement
            || $doc.mozPointerLockElement) != null;
    }-*/;

    private native boolean requestPointerLock()/*-{
        var canvas = $doc.getElementById("main");
        var f = (canvas.requestPointerLock
            || canvas.webkitRequestPointerLock
            || canvas.mozRequestPointerLock);
        if (f == null) {
            return false;
        }
        f.bind(canvas)();
        return true;
    }-*/;

    private native void registerNativeEvents()/*-{
        var that = this;
        $doc.getElementById("main").onclick = function (e) {
            e.preventDefault();
            that.@uk.co.thinkofdeath.mapviewer.client.input.InputManager::onCanvasClick(II)(e.clientX, e.clientY);
        };
        $doc.onmousemove = function (e) {
            var mx = (e.movementX || e.webkitMovementX || e.mozMovementX);
            var my = (e.movementY || e.webkitMovementY || e.mozMovementY);
            if (mx == undefined || my == undefined) {
                that.@uk.co.thinkofdeath.mapviewer.client.input.InputManager::onMouseMove(II)(e.clientX, e.clientY);
            } else {
                that.@uk.co.thinkofdeath.mapviewer.client.input.InputManager::onMouseLockMove(II)(mx, my);
            }
        };
        $doc.onkeydown = function (e) {
            var key = e.keyCode || e.which;
            if (that.@uk.co.thinkofdeath.mapviewer.client.input.InputManager::onKeyDown(C)(key)) {
                e.preventDefault();
            }
        };
        $doc.onkeyup = function (e) {
            var key = e.keyCode || e.which;
            if (that.@uk.co.thinkofdeath.mapviewer.client.input.InputManager::onKeyUp(C)(key)) {
                e.preventDefault();
            }
        };
    }-*/;
}

@@ -2009,24 +2009,25 @@
 tationY() + 
+(
 movementX / 
@@ -2024,22 +2024,26 @@
 vementX 
-/ 300f
+* 0.0025f)
 );%0A     
@@ -2089,16 +2089,17 @@
 onX() + 
+(
 movement
@@ -2104,14 +2104,18 @@
 ntY 
-/ 300f
+* 0.0025f)
 );%0A 
@@ -2153,32 +2153,46 @@
 ) %3E Math.PI / 2)
+ %7B%0A           
  camera.setRotat
@@ -2216,24 +2216,34 @@
 h.PI / 2));%0A
+        %7D%0A
         if (
@@ -2279,16 +2279,30 @@
 .PI / 2)
+ %7B%0A           
  camera.
@@ -2335,24 +2335,314 @@
 h.PI / 2));%0A
+        %7D%0A%0A        while (camera.getRotationY() %3C 0) %7B%0A            camera.setRotationY((float) (camera.getRotationY() + Math.PI * 2));%0A        %7D%0A        while (camera.getRotationY() %3E= Math.PI * 2) %7B%0A            camera.setRotationY((float) (camera.getRotationY() - Math.PI * 2));%0A        %7D%0A
     %7D%0A%0A    p
@@ -4280,32 +4280,64 @@
  function (e) %7B%0A
+            e.preventDefault();%0A
             var 
@@ -4490,13 +4490,147 @@
 if (
+($doc.pointerLockElement%0A                %7C%7C $doc.webkitPointerLockElement%0A                %7C%7C $doc.mozPointerLockElement) === null %7C%7C 
 mx ==
+=
  und
@@ -4644,16 +4644,17 @@
 %7C%7C my ==
+=
  undefin

Improve the camera