#ifndef WINDOW_H
#define WINDOW_H

#include "GL/glew.h"
#include "GLFW/glfw3.h"

#include "input/Buttons.h"
#include "input/TextInput.h"
#include "rendering/WindowOptions.h"
#include "utils/Clock.h"
#include "utils/Error.h"

class Window final {
    GLFWwindow* window;
    Clock fps;
    Clock tps;
    Size size;

public:
    TextInput* textInput;
    Buttons buttons;

    Window();
    ~Window();
    Window(const Window&) = delete;
    Window& operator=(const Window&) = delete;
    Window(Window&&) = delete;
    Window& operator=(Window&&) = delete;

    Error open(const WindowOptions& options);

    const Clock& getFrameClock() const;
    const Clock& getTickClock() const;
    const Size& getSize() const;

    void trapCursor(bool trap);

    template<typename T>
    void run(T& game, Clock::Nanos nanosPerTick) {
        glfwShowWindow(window);
        Clock::Nanos lag = 0;
        while(!glfwWindowShouldClose(window) && game.isRunning()) {
            lag += fps.update();
            while(lag >= nanosPerTick) {
                lag -= nanosPerTick;
                tps.update();
                buttons.tick();
                game.tick();
            }
            game.render(static_cast<float>(lag) / nanosPerTick);
            glfwSwapBuffers(window);
            glfwPollEvents();
        }
    }

private:
    static void onKey(GLFWwindow* w, int key, int scancode, int action,
                      int mods);
    static void onChar(GLFWwindow* w, unsigned int codepoint);
    static void onResize(GLFWwindow* w, int width, int height);
    static void onMouse(GLFWwindow* w, int button, int action, int mods);
    static void onMouseMove(GLFWwindow* w, double x, double y);
};

#endif