//
//  hmdControls.js
//  examples
//
//  Created by Sam Gondelman on 6/17/15
//  Copyright 2015 High Fidelity, Inc.
//
//  Distributed under the Apache License, Version 2.0.
//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//

var MOVE_DISTANCE = 2.0;
var PITCH_INCREMENT = 0.5; // degrees
var pitchChange = 0; // degrees
var YAW_INCREMENT = 0.5; // degrees
var VR_YAW_INCREMENT = 15.0; // degrees
var yawChange = 0;
var BOOM_SPEED = 0.5;
var THRESHOLD = 0.2;

var CAMERA_UPDATE_TIME = 0.5;
var yawTimer = CAMERA_UPDATE_TIME;

var shifted = false;
var SHIFT_UPDATE_TIME = 0.5;
var shiftTimer = SHIFT_UPDATE_TIME;
var SHIFT_MAG = 4.0;

var warpActive = false;
var WARP_UPDATE_TIME = .5;
var warpTimer = WARP_UPDATE_TIME;

var warpPosition = { x: 0, y: 0, z: 0 };

var WARP_SPHERE_SIZE = 1;
var warpSphere = Overlays.addOverlay("sphere", {
    position: { x: 0, y: 0, z: 0 },
    size: WARP_SPHERE_SIZE,
    color: { red: 0, green: 255, blue: 0 },
    alpha: 1.0,
    solid: true,
    visible: false,
});

var WARP_LINE_HEIGHT = 10;
var warpLine = Overlays.addOverlay("line3d", {
    start: { x: 0, y: 0, z:0 },
    end: { x: 0, y: 0, z: 0 },
    color: { red: 0, green: 255, blue: 255},
    alpha: 1,
    lineWidth: 5,
    visible: false,
});

var velocity = { x: 0, y: 0, z: 0 };
var VERY_LONG_TIME = 1000000.0;

var active = HMD.active;
var prevVRMode = HMD.active;

var hmdControls = (function () {

    function onKeyPressEvent(event) {
        if (event.text == 'g' && event.isMeta) {
            active = !active;
        }
    }

    function findAction(name) {
        var actions = Controller.getAllActions();
        for (var i = 0; i < actions.length; i++) {
            if (actions[i].actionName == name) {
                return i;
            }
        }
        // If the action isn't found, it will default to the first available action
        return 0;
    }

    function onActionEvent(action, state) {
        if (!active) {
            return;
        }
        if (state < THRESHOLD) {
            if (action == findAction("YAW_LEFT") || action == findAction("YAW_RIGHT")) {
                yawTimer = CAMERA_UPDATE_TIME;
            } else if (action == findAction("PITCH_UP") || action == findAction("PITCH_DOWN")) {
                pitchTimer = CAMERA_UPDATE_TIME;
            }
            return;
        }
        switch (action) {
            case findAction("LONGITUDINAL_BACKWARD"):
                var direction = {x: 0.0, y: 0.0, z:1.0};
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("LONGITUDINAL_FORWARD"):
                var direction = {x: 0.0, y: 0.0, z:-1.0};
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("LATERAL_LEFT"):
                var direction = {x:-1.0, y: 0.0, z: 0.0}
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("LATERAL_RIGHT"):
                var direction = {x:1.0, y: 0.0, z: 0.0};
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("VERTICAL_DOWN"):
                var direction = {x: 0.0, y: -1.0, z: 0.0};
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("VERTICAL_UP"):
                var direction = {x: 0.0, y: 1.0, z: 0.0};
                direction = Vec3.multiply(Vec3.normalize(direction), shifted ? SHIFT_MAG * MOVE_DISTANCE : MOVE_DISTANCE);
                velocity = Vec3.sum(velocity, direction);
                break;
            case findAction("YAW_LEFT"):
                if (yawTimer < 0.0 && HMD.active) {
                    yawChange = yawChange + (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
                    yawTimer = CAMERA_UPDATE_TIME;
                } else if (!HMD.active) {
                    yawChange = yawChange + (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
                }
                break;
            case findAction("YAW_RIGHT"):
                if (yawTimer < 0.0 && HMD.active) {
                    yawChange = yawChange - (shifted ? SHIFT_MAG * VR_YAW_INCREMENT : VR_YAW_INCREMENT);
                    yawTimer = CAMERA_UPDATE_TIME;
                } else if (!HMD.active) {
                    yawChange = yawChange - (shifted ? SHIFT_MAG * YAW_INCREMENT : YAW_INCREMENT);
                }
                break;
            case findAction("PITCH_DOWN"):
                if (!HMD.active) {
                    pitchChange = pitchChange - (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
                }
                break;
            case findAction("PITCH_UP"):
                if (!HMD.active) {
                    pitchChange = pitchChange + (shifted ? SHIFT_MAG * PITCH_INCREMENT : PITCH_INCREMENT);
                }
                break;
            case findAction("SHIFT"): // speed up
                if (shiftTimer < 0.0) {
                    shifted = !shifted;
                    shiftTimer = SHIFT_UPDATE_TIME;
                }
                break;
            case findAction("ACTION1"): // start/end warp
                if (warpTimer < 0.0) {
                    warpActive = !warpActive;
                    if (!warpActive) {
                        finishWarp();
                    }
                    warpTimer = WARP_UPDATE_TIME;
                }
                break;
            case findAction("ACTION2"): // cancel warp
                warpActive = false;
                Overlays.editOverlay(warpSphere, {
                    visible: false,
                });
                Overlays.editOverlay(warpLine, {
                    visible: false,
                });
            default:
                break;
        }
    }

    function update(dt) {
        if (prevVRMode != HMD.active) {
            active = HMD.active;
            prevVRMode = HMD.active;
        }

        if (yawTimer >= 0.0) {
            yawTimer = yawTimer - dt;
        }
        if (shiftTimer >= 0.0) {
            shiftTimer = shiftTimer - dt;
        }
        if (warpTimer >= 0.0) {
            warpTimer = warpTimer - dt;
        }

        if (warpActive) {
            updateWarp();
        }

        if (active) {
            Controller.captureActionEvents();

            MyAvatar.bodyYaw = MyAvatar.bodyYaw + yawChange;
            MyAvatar.headPitch = Math.max(-180, Math.min(180, MyAvatar.headPitch + pitchChange));
            yawChange = 0;
            pitchChange = 0;

            MyAvatar.motorVelocity = velocity;
            MyAvatar.motorTimescale = 0.0;
            velocity = { x: 0, y: 0, z: 0 };
        } else {
            Controller.releaseActionEvents();
            yawChange = 0;
            pitchChange = 0;
            MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
            MyAvatar.motorTimescale = VERY_LONG_TIME;
        }
    }

    function updateWarp() {
        var look = Quat.getFront(Camera.getOrientation());
        var pitch = Math.asin(look.y);

        // Get relative to looking straight down
        pitch += Math.PI / 2;

        // Scale up
        pitch *= 2;
        var distance = pitch * pitch * pitch;

        var warpDirection = Vec3.normalize({ x: look.x, y: 0, z: look.z });
        warpPosition = Vec3.multiply(warpDirection, distance);
        warpPosition = Vec3.sum(MyAvatar.position, warpPosition);

        // Commented out until ray picking can be fixed
        // var pickRay = {
        //     origin: Vec3.sum(warpPosition, WARP_PICK_OFFSET),
        //     direction: { x: 0, y: -1, z: 0 }
        // };

        // var intersection = Entities.findRayIntersection(pickRay);

        // if (intersection.intersects && intersection.distance < WARP_PICK_MAX_DISTANCE) {
        //     // Warp 1 meter above the object - this is an approximation
        //     // TODO Get the actual offset to the Avatar's feet and plant them to
        //     // the object.
        //     warpPosition = Vec3.sum(intersection.intersection, { x: 0, y: 1, z:0 });
        // }

        // Adjust overlays to match warp position
        Overlays.editOverlay(warpSphere, {
            position: warpPosition,
            visible: true,
        });
        Overlays.editOverlay(warpLine, {
            start: warpPosition,
            end: Vec3.sum(warpPosition, { x: 0, y: WARP_LINE_HEIGHT, z: 0 }),
            visible: true,
        });
    }

    function finishWarp() {
        Overlays.editOverlay(warpSphere, {
            visible: false,
        });
        Overlays.editOverlay(warpLine, {
            visible: false,
        });
        MyAvatar.position = warpPosition;
    }

    function setUp() {
        Controller.keyPressEvent.connect(onKeyPressEvent);

        Controller.actionEvent.connect(onActionEvent);

        Script.update.connect(update);
    }

    function tearDown() {
        Controller.releaseActionEvents();
        MyAvatar.motorVelocity = {x:0.0, y:0.0, z:0.0}
        MyAvatar.motorTimescale = VERY_LONG_TIME;
    }

    setUp();
    Script.scriptEnding.connect(tearDown);
}());