/*
 * Copyright (C) 2013 Darth Affe <http://wyrez.org> and contributors
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.wyrez.shootingstars.controls;

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.scene.Spatial;
import com.jme3.scene.control.Control;
import org.wyrez.shootingstars.helper.UserDataKeys;

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

    private static final String MAPPING_PLAYER_LEFT = "PLAYER_Left";
    private static final String MAPPING_PLAYER_RIGHT = "PLAYER_Right";
    private static final String MAPPING_PLAYER_UP = "PLAYER_UP";
    private static final String MAPPING_PLAYER_DOWN = "PLAYER_Down";
    private static final float PITCH_LIMIT = 1.4f;
    /**/
    private float speed = 2f;
    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();
    }

    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 (spatial.getUserData(UserDataKeys.RUNNING)) {
            if (name.equals(MAPPING_PLAYER_LEFT)) {
                rotateCamera(value, initialUpVec);
            } else if (name.equals(MAPPING_PLAYER_RIGHT)) {
                rotateCamera(-value, initialUpVec);
            } else if (name.equals(MAPPING_PLAYER_UP)) {
                rotateCamera(-value, spatial.getLocalRotation().getRotationColumn(0));
            } else if (name.equals(MAPPING_PLAYER_DOWN)) {
                rotateCamera(value, spatial.getLocalRotation().getRotationColumn(0));
            }
        }
    }

    private void initMappings() {
        inputManager.addMapping(MAPPING_PLAYER_LEFT, new MouseAxisTrigger(MouseInput.AXIS_X, true),
                new KeyTrigger(KeyInput.KEY_LEFT));

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

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

        inputManager.addMapping(MAPPING_PLAYER_DOWN, new MouseAxisTrigger(MouseInput.AXIS_Y, true),
                new KeyTrigger(KeyInput.KEY_DOWN));
        inputManager.addListener(this, new String[]{MAPPING_PLAYER_LEFT,
                    MAPPING_PLAYER_RIGHT, MAPPING_PLAYER_UP, MAPPING_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] < -PITCH_LIMIT) {
            angles[0] = -PITCH_LIMIT;
            spatial.setLocalRotation(new Quaternion(angles));
        } else if (angles[0] > PITCH_LIMIT) {
            angles[0] = PITCH_LIMIT;
            spatial.setLocalRotation(new Quaternion(angles));
        } else {
            spatial.setLocalRotation(q);
        }
    }
}
