#include "ogl.hpp"


bool glerr(const char* prefix, bool always) {
    const char* estr = "[UNKNOWN]"; // TODO: gluErrorString(e)
    GLenum e = glGetError();
    switch (e) {
        case GL_NO_ERROR:
            if (!always) return false;
            estr = "GL_NO_ERROR";
            break;
        case GL_INVALID_ENUM:
            estr = "GL_INVALID_ENUM";
            break;
        case GL_INVALID_VALUE:
            estr = "GL_INVALID_VALUE";
            break;
        case GL_INVALID_OPERATION:
            estr = "GL_INVALID_OPERATION";
            break;
        case GL_STACK_OVERFLOW:
            estr = "GL_STACK_OVERFLOW";
            break;
        case GL_STACK_UNDERFLOW:
            estr = "GL_STACK_UNDERFLOW";
            break;
        case GL_OUT_OF_MEMORY:
            estr = "GL_OUT_OF_MEMORY";
            break;
    }
    LOG("%sGL err 0x%x - %s", prefix?:"", e, estr);
    return true;
}


Window* Window::inst = NULL;


static GLfloat scale(unsigned val, unsigned max) {
    assert(val<=max);
    const GLfloat mid = ((GLfloat)max) / 2.0f;
    return (((GLfloat)val) - mid) / mid;
}


GLfloat Window::px2iso_x(unsigned x) const {
    return scale(x, w-1);
}


GLfloat Window::px2iso_y(unsigned y) const {
    return scale(y, h-1);
}


unsigned Window::iso2px_x(GLfloat x) const {
    return ((x+1.0f)*0.5f) * (GLfloat)w;
}


unsigned Window::iso2px_y(GLfloat y) const {
    return ((y+1.0f)*0.5f) * (GLfloat)h;
}


Window* Window::getInst(unsigned w, unsigned h, bool fullscreen, const char* name) {
    if (inst) return NULL; // singleton and only once atm

    glfwInit();

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    const GLFWvidmode* mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
    if (fullscreen) {
        w = mode->width;
        h = mode->height;
    } else if (!w || !h) { // windowed w/o explicit dimensions
        w = mode->width / 2;
        h = mode->height / 2;
    } else {
        w = MIN((int)w, mode->width);
        h = MIN((int)h, mode->height);
    }
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
    GLFWwindow* win = glfwCreateWindow(w, h, name?:"OpenGL", fullscreen? glfwGetPrimaryMonitor(): NULL, NULL); // TODO: error detection
    glfwMakeContextCurrent(win);

    glfwSwapInterval(1); // limit framerate to vsync

    glewExperimental = GL_TRUE;
    if (glewInit() != GLEW_OK) {
        return NULL;
    }
    (void)glGetError(); // https://www.opengl.org/wiki/OpenGL_Loading_Library#Initialization_of_GLEW_1.13.0_and_earlier

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    //glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glEnable(GL_DEPTH_TEST); // TODO: only if needed?

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    return inst = new Window(win, w, h);
}


Window::~Window() {
    glfwDestroyWindow(window);
    glfwTerminate();
}


void Window::shouldClose(bool b) {
    glfwSetWindowShouldClose(window, b? GL_TRUE: GL_FALSE);
}


bool Window::shouldClose() const {
    return glfwWindowShouldClose(window);
}