#include "Controllable.hpp"

Controllable::Controllable(GLFWwindow *window, const InputMap& inputs, const Controllable::options& opt): window(window), inputs(inputs), o(opt){ }
Controllable::~Controllable() { }

glm::vec3 Controllable::getDirection() const {
    return glm::vec3(cos(VerticalAngle) * sin(HorizontalAngle), sin(VerticalAngle), cos(VerticalAngle) * cos(HorizontalAngle));
}
Controllable::axis Controllable::getAxis() const {
    Controllable::axis a;
    // Direction : Spherical coordinates to Cartesian coordinates conversion
    a.direction = getDirection();

    // Right vector
    a.right = glm::vec3(sin(HorizontalAngle - 3.14f / 2.0f), 0, cos(HorizontalAngle - 3.14f / 2.0f));

    // Up vector
    a.up = glm::cross(a.right, a.direction);
    return a;
}

void Controllable::capture(bool readMouse, bool captureKeys, float deltaTime, bool freeMouse) {
    // Get mouse position
    if(readMouse) {
        if(capturingMouse) {
            double xPos, yPos;
            glfwGetCursorPos(window, &xPos, &yPos);

            // Compute new orientation
            HorizontalAngle += o.sensibility * 0.0001f * float(mouseX - xPos);
            VerticalAngle += o.sensibility * 0.0001f * float(mouseY - yPos);
            mouseX = xPos;
            mouseY = yPos;
        } else {
            if (!freeMouse)
                glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
            capturingMouse = true;
            glfwGetCursorPos(window, &mouseX, &mouseY);
        }
        // Reset mouse position for next frame
        if (!freeMouse) {
            int viewportX, viewportY;
            glfwGetWindowSize(window, &viewportX, &viewportY);
            mouseX = viewportX / 2;
            mouseY = viewportY / 2;
            glfwSetCursorPos(window, mouseX, mouseY);
        }
    } else if (capturingMouse) {
        glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
        capturingMouse = false;
    }

    const auto axis = getAxis();

    velocity = glm::vec3(0);
    if(captureKeys) {
        // Move forward
        if (inputs.isDown(Input::Forward)) {
            velocity += axis.direction;
        }
        // Move backward
        if (inputs.isDown(Input::Backward)) {
            velocity -= axis.direction;
        }
        // Strafe right
        if (inputs.isDown(Input::Right)) {
            velocity += axis.right;
        }
        // Strafe left
        if (inputs.isDown(Input::Left)) {
            velocity -= axis.right;
        }
        // Move up
        if (inputs.isDown(Input::Up)) {
            velocity += axis.up;
        }
        // Move down
        if (inputs.isDown(Input::Down)) {
            velocity -= axis.up;
        }
        if(velocity != glm::vec3(0)) {
            velocity = glm::normalize(velocity) * deltaTime * o.speed;
        }
    }
}