package mygame;

import com.jme3.input.InputManager;
import com.jme3.input.KeyInput;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.AnalogListener;
import com.jme3.input.controls.KeyTrigger;
import com.jme3.input.controls.MouseAxisTrigger;
import com.jme3.math.Matrix3f;
import com.jme3.math.Quaternion;
import com.jme3.math.Vector3f;
import com.jme3.renderer.Camera;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Spatial;
import com.jme3.scene.control.AbstractControl;
import com.jme3.scene.control.Control;

/**
 *
 * @author Snowsun
 */
public class PlayerMouseControl extends AbstractControl implements AnalogListener {

    private float speed;
    private InputManager inputManager;
    private Camera cam;
    private Vector3f initialUpVec;

    public PlayerMouseControl(InputManager inputManager, Camera cam) {
        this.inputManager = inputManager;
        this.cam = cam;
        initMappings();
    }

    @Override
    public void setSpatial(Spatial spatial) {
        super.setSpatial(spatial);
        initialUpVec = spatial.getLocalRotation().getRotationColumn(1).clone();
    }

    @Override
    protected void controlUpdate(float tpf) {
    }

    @Override
    protected void controlRender(RenderManager rm, ViewPort vp) {
    }

    public Control cloneForSpatial(Spatial spatial) {
        PlayerMouseControl control = new PlayerMouseControl(inputManager,cam);
        spatial.addControl(control);
        return control;
    }

    public void setSpeed(float speed) {
        this.speed = speed;
    }

    public float getSpeed() {
        return speed;
    }

    public void onAnalog(String name, float value, float tpf) {
        if (name.equals("PLAYER_Left")) {
            rotateCamera(value, initialUpVec);
        } else if (name.equals("PLAYER_Right")) {
            rotateCamera(-value, initialUpVec);
        } else if (name.equals("PLAYER_Up")) {
            rotateCamera(-value, spatial.getLocalRotation().getRotationColumn(0));
        } else if (name.equals("PLAYER_Down")) {
            rotateCamera(value, spatial.getLocalRotation().getRotationColumn(0));
        }
    }
    
    private void initMappings() {
        inputManager.addMapping("PLAYER_Left", new MouseAxisTrigger(MouseInput.AXIS_X, true),
                new KeyTrigger(KeyInput.KEY_LEFT));

        inputManager.addMapping("PLAYER_Right", new MouseAxisTrigger(MouseInput.AXIS_X, false),
                new KeyTrigger(KeyInput.KEY_RIGHT));

        inputManager.addMapping("PLAYER_Up", new MouseAxisTrigger(MouseInput.AXIS_Y, false),
                new KeyTrigger(KeyInput.KEY_UP));

        inputManager.addMapping("PLAYER_Down", new MouseAxisTrigger(MouseInput.AXIS_Y, true),
                new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addListener(this, new String[]{"PLAYER_Left","PLAYER_Right","PLAYER_Up","PLAYER_Down"});
    }
    
    private void rotateCamera(float value, Vector3f axis) {        
        Matrix3f mat = new Matrix3f();
        mat.fromAngleNormalAxis(speed * value, axis);

        
        Vector3f up = spatial.getLocalRotation().getRotationColumn(1);
        Vector3f left = spatial.getLocalRotation().getRotationColumn(0);
        Vector3f dir = spatial.getLocalRotation().getRotationColumn(2);

        mat.mult(up, up);
        mat.mult(left, left);
        mat.mult(dir, dir);

        Quaternion q = new Quaternion();
        q.fromAxes(left, up, dir);
        q.normalizeLocal();

        float[] angles = q.toAngles(null);
        if (angles[0] < -1.4f) {
            angles[0] = -1.4f;
            spatial.setLocalRotation(new Quaternion(angles));
        } else if (angles[0] > 1.4f) {
            angles[0] = 1.4f;
            spatial.setLocalRotation(new Quaternion(angles));
        } else {
            spatial.setLocalRotation(q);
        }
    }

}
