#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <GLFW/glfw3.h>
#include "LatticeWindow.h"

volatile static unsigned int WindowCount = 0;

// typedef'ed in LatticeWindow.h
struct LatticeWindow{
    GLFWwindow *window;
    WindowState state;
    unsigned int width;
    unsigned int height;
    double lastFPSTime;
    unsigned int fpsFrames;
};

/*******************************
 * Private Data
 ******************************/
LatticeWindow Window;

/*******************************
 * Private Function Declarations
 ******************************/

static void error_cb(int error, const char* description);
static void close_cb(GLFWwindow *window);
static GLFWwindow *init_window(void);

/*******************************
 * Public Function Definitions
 ******************************/

// Note - NOT threadsafe
LatticeWindow *CreateWindow(unsigned int width, unsigned int height) {
    if(!WindowCount) {
        if (!glfwInit()) {
            return(NULL);
        }
        glfwSetErrorCallback(error_cb);
    }
    else {
        /* only one window at a time currently supported */
        return NULL;
    }

    Window.window = glfwCreateWindow(width, height,
                                      "GLFW Test", NULL, NULL);
    if(!Window.window) {
        if(!WindowCount) {
            glfwTerminate();
        }
        return NULL;
    }
    glfwSetWindowCloseCallback(Window.window, close_cb);
    glfwMakeContextCurrent(Window.window);
    glfwSwapInterval(1);
    Window.state = WINDOW_OPEN;
    Window.width = width;
    Window.height = height;
    Window.lastFPSTime = glfwGetTime();
    Window.fpsFrames = 0;
    WindowCount++;
    return &Window;
}

// Note - NOT threadsafe
void DestroyWindow(LatticeWindow *window) {
    if(!window) {
        return;
    }

    if(window->window) {
        glfwDestroyWindow(window->window);
        WindowCount--;
        if(!WindowCount) {
            glfwTerminate();
        }
    }
}

WindowState GetWindowState(LatticeWindow *window) {
    return window->state;
}

void DrawFrame(LatticeWindow *window, void *buf,
               unsigned int width, unsigned int height) {
    double now;
    if(width != window->width || height != window->height) {
        fputs("Tried to draw invalid frame size", stderr);
        return;
    }
    glfwPollEvents();
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glWindowPos2s(0, 0);
    glDrawPixels(width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf);
    //usleep(10000);
    glfwSwapBuffers(window->window);
    window->fpsFrames++;

    now = glfwGetTime();
    if (now - window->lastFPSTime >= 2.0 ){ // If last prinf() was more than 2 sec ago
        // printf and reset timer
        printf("%f ms/frame (%f fps)\n", 2000.0/window->fpsFrames, window->fpsFrames / 2.0);
        window->fpsFrames = 0;
        window->lastFPSTime += 2.0;
    }
}

/*******************************
 * Private Function Definitions
 ******************************/

static void error_cb(int error, const char* description) {
    fputs(description, stderr);
}

static void close_cb(GLFWwindow *window) {
    assert(Window.window = window);
    Window.state = WINDOW_SHOULD_CLOSE;
}
