#include "input.hpp"


Input* Input::inst = NULL;
const size_t Input::maxevents = 10;


Input::Input(Window* w): window(w) {
    assert(!inst);
    inst = this;
    //glfwSetInputMode(window, GLFW_STICKY_KEYS, 1);
    glfwSetKeyCallback(window->window, &keyFun);
    glfwSetMouseButtonCallback(window->window, &clickFun);
    glfwSetScrollCallback(window->window, &scrollFun);
}


void Input::shouldClose() {
    window->shouldClose(true);
}


void Input::keyFun(GLFWwindow* window, int key, int, int action, int) {
    assert(window == inst->window->window);
    if (action != GLFW_PRESS && action != GLFW_REPEAT) return;
    switch (key) {
        case GLFW_KEY_ESCAPE:
            inst->window->shouldClose(true);
            break;
        case GLFW_KEY_PAGE_UP:
            inst->pans++;
            break;
        case GLFW_KEY_PAGE_DOWN:
            inst->pans--;
            break;
        case GLFW_KEY_RIGHT:
        case GLFW_KEY_D:
            if (inst->poskeys.size() < inst->maxevents) inst->poskeys.push((poskey_t){1, 0});
            break;
        case GLFW_KEY_LEFT:
        case GLFW_KEY_A:
            if (inst->poskeys.size() < inst->maxevents) inst->poskeys.push((poskey_t){-1, 0});
            break;
        case GLFW_KEY_UP:
        case GLFW_KEY_W:
            if (inst->poskeys.size() < inst->maxevents) inst->poskeys.push((poskey_t){0, 1});
            break;
        case GLFW_KEY_DOWN:
        case GLFW_KEY_S:
            if (inst->poskeys.size() < inst->maxevents) inst->poskeys.push((poskey_t){0, -1});
            break;
    }
}


void Input::clickFun(GLFWwindow* window, int button, int action, int) {
    assert(window == inst->window->window);
    if (action != GLFW_RELEASE || button != GLFW_MOUSE_BUTTON_LEFT) {
        return;
    }
    if (inst->clicks.size() >= inst->maxevents) {
        return;
    }

    double xpos, ypos;
    float zpos;
    glfwGetCursorPos(window, &xpos, &ypos);
    if (xpos < 0.0 || xpos >= inst->window->w || ypos < 0.0 || ypos >= inst->window->h) {
        return;
    }
    ypos = ((double)inst->window->h) - ypos;

    glReadPixels(floor(xpos), floor(ypos), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &zpos);
    if (zpos < 0.0 || zpos >= 1.0) {
        return;
    }

    inst->clicks.push((click_t){xpos, ypos, zpos});
}


void Input::scrollFun(GLFWwindow* window, double, double yoffset) {
    assert(window == inst->window->window);
    inst->pans += yoffset;
}


bool Input::getPanKey(int& p) {
    p = pans;
    pans = 0;
    return (p != 0);
}


bool Input::getPosKey(int& x, int& y) {
    if (poskeys.empty()) return false;
    x = poskeys.front().x;
    y = poskeys.front().y;
    poskeys.pop();
    return true;
}


bool Input::getClick(double& x, double& y, float& z) {
    if (clicks.empty()) return false;
    x = clicks.front().x;
    y = clicks.front().y;
    z = clicks.front().z;
    clicks.pop();
    return true;
}