+#include "components/point.hpp"
+#include "components/player.hpp"
+#include "components/texture.hpp"
+#include "components/velocity.hpp"
+#include "window.hpp"
+
 #include <chrono>
-#include <cstdlib>
-#include <iostream>
-#include <string_view>
 #include <thread>
 
 #include <entt/entt.hpp>
 #include <SDL2/SDL.h>
-#include <SDL2/SDL_image.h>
-
-static constexpr auto WINDOW_WIDTH  = 640;
-static constexpr auto WINDOW_HEIGHT = 480;
-
-static SDL_Window *window = nullptr;
-static SDL_Renderer *renderer = nullptr;
-
-static int sdl2Initialize();
-static SDL_Texture *sdl2LoadTexture(const char *path);
-
-struct Player {
-    char unused = 0;
-};
-
-struct Vec2 {
-    float x, y;
-
-    auto& operator+=(const Vec2& o) noexcept {
-        x += o.x;
-        y += o.y;
-        return *this;
-    }
-};
-
-struct Point : public Vec2 {};
-struct Velocity : public Vec2 {};
 
-class Texture
-{
-public:
-    Texture(const char *path) {
-        tex = sdl2LoadTexture(path);
-    }
+constexpr std::chrono::microseconds FRAME_TIME (1'000'000 / 60);
 
-    ~Texture() {
-        if (tex != nullptr)
-            SDL_DestroyTexture(tex);
-    }
-
-    void operator()(SDL_Renderer *rend, Point p) const noexcept {
-        const int x = static_cast<int>(p.x);
-        const int y = static_cast<int>(p.y);
-        SDL_Rect rect {x, y, 0, 0};
-
-        /* TODO err check */
-        SDL_QueryTexture(tex, nullptr, nullptr, &rect.w, &rect.h);
-        SDL_RenderCopy(rend, tex, nullptr, &rect);
-    }
-
-private:
-    SDL_Texture *tex;
-};
+static bool handleInputs(entt::registry& registry);
 
 int main()
 {
     registry.emplace<Velocity>(ent, 0.f, 0.f);
     registry.emplace<Texture>(ent, "img/player.png");
 
-    SDL_Event e;
-    bool quit = false;
-    while (!quit) {
-        const auto nextFrame = std::chrono::high_resolution_clock::now() +
-            std::chrono::microseconds(1'000'000 / 60);
+    do {
+        const auto now = std::chrono::high_resolution_clock::now();
 
         SDL_RenderClear(renderer);
-        registry.view<Texture, Point>().each(
-            [](Texture& tex, Point& p) { tex(renderer, p); });
+        registry.view<Texture, Point>().each([](auto& tex, auto& p) { tex(renderer, p); });
         SDL_RenderPresent(renderer);
 
-        registry.view<Velocity, Point>().each(
-            [](Velocity& v, Point& p) { p += v; });
-
-        while (SDL_PollEvent(&e)) {
-            if (e.type == SDL_QUIT) {
-                quit = true;
-            } else if (e.type == SDL_KEYDOWN) {
-                auto view = registry.view<Player, Velocity>();
-
-                if (e.key.repeat) {
-                    // do nothing
-                } else if (e.key.keysym.sym == SDLK_d) {
-                    view.each([](Player& p, Velocity& v) { v.x += 1.5f; });
-                } else if (e.key.keysym.sym == SDLK_a) {
-                    view.each([](Player& p, Velocity& v) { v.x -= 1.5f; });
-                }
-            } else if (e.type == SDL_KEYUP) {
-                auto view = registry.view<Player, Velocity>();
-
-                if (e.key.repeat) {
-                    // do nothing
-                } else if (e.key.keysym.sym == SDLK_d) {
-                    view.each([](Player& p, Velocity& v) { v.x -= 1.5f; });
-                } else if (e.key.keysym.sym == SDLK_a) {
-                    view.each([](Player& p, Velocity& v) { v.x += 1.5f; });
-                }
-            }
-        }
-
-        std::this_thread::sleep_until(nextFrame);
-    }
+        registry.view<Velocity, Point>().each([](auto& v, auto& p) { p += v; });
 
-    SDL_DestroyWindow(window);
-    SDL_Quit();
+        std::this_thread::sleep_until(now + FRAME_TIME);
+    } while (handleInputs(registry));
 }
 
-int sdl2Initialize()
+bool handleInputs(entt::registry& registry)
 {
-    if (auto err = SDL_Init(SDL_INIT_VIDEO); err < 0) {
-        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
-        return EXIT_FAILURE;
-    } else {
-        atexit(SDL_Quit);
-    }
-
-    if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) {
-        std::cerr << "IMG error: " << IMG_GetError() << std::endl;
-        return EXIT_FAILURE;
-    } else {
-        atexit(IMG_Quit);
-    }
-
-    window = SDL_CreateWindow("game",
-        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 
-        WINDOW_WIDTH, WINDOW_HEIGHT,
-        SDL_WINDOW_SHOWN);
-
-    if (window == nullptr) {
-        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
-        return EXIT_FAILURE;
-    } else {
-        atexit([] { SDL_DestroyWindow(window); });
-    }
-
-    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
-
-    if (renderer == nullptr) {
-        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
-        return EXIT_FAILURE;
-    } else {
-        atexit([] { SDL_DestroyRenderer(renderer); });
-    }
-
-    return 0;
-}
+    bool quit = false;
 
-SDL_Texture *sdl2LoadTexture(const char *path)
-{
-    SDL_Texture *tex = nullptr;
-
-    auto surface = IMG_Load(path);
-    if (surface == nullptr) {
-        std::cerr << "Unable to load image " << path << '!' << std::endl;
-        std::cerr << "SDL error: " << IMG_GetError() << std::endl;
-    } else {
-        tex = SDL_CreateTextureFromSurface(renderer, surface);
-
-        if (tex == nullptr) {
-            std::cerr << "Unable to create texture for " << path << '!' << std::endl;
-            std::cerr << "SDL error: " << SDL_GetError() << std::endl;
-        } else {
-            SDL_FreeSurface(surface);
+    for (SDL_Event e; SDL_PollEvent(&e);) {
+        if (e.type == SDL_QUIT) {
+            quit = true;
+        } else if (e.type == SDL_KEYDOWN && !e.key.repeat) {
+            auto view = registry.view<Player, Velocity>();
+
+            switch (e.key.keysym.sym) {
+            case SDLK_d:
+                view.each([](Player& p, Velocity& v) { v.x += 1.5f; });
+                break;
+            case SDLK_a:
+                view.each([](Player& p, Velocity& v) { v.x -= 1.5f; });
+                break;
+            }
+        } else if (e.type == SDL_KEYUP && !e.key.repeat) {
+            auto view = registry.view<Player, Velocity>();
+
+            switch (e.key.keysym.sym) {
+            case SDLK_d:
+                view.each([](Player& p, Velocity& v) { v.x -= 1.5f; });
+                break;
+            case SDLK_a:
+                view.each([](Player& p, Velocity& v) { v.x += 1.5f; });
+                break;
+            }
         }
     }
 
-    return tex;
+    return !quit;
 }
 
 
--- /dev/null
+#include "window.hpp"
+
+#include <cstdlib>
+#include <iostream>
+
+#include <SDL2/SDL_image.h>
+
+SDL_Window *window;
+SDL_Renderer *renderer;
+
+int sdl2Initialize()
+{
+    if (auto err = SDL_Init(SDL_INIT_VIDEO); err < 0) {
+        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
+        return EXIT_FAILURE;
+    } else {
+        atexit(SDL_Quit);
+    }
+
+    if (!(IMG_Init(IMG_INIT_PNG) & IMG_INIT_PNG)) {
+        std::cerr << "IMG error: " << IMG_GetError() << std::endl;
+        return EXIT_FAILURE;
+    } else {
+        atexit(IMG_Quit);
+    }
+
+    window = SDL_CreateWindow("game",
+        SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 
+        WINDOW_WIDTH, WINDOW_HEIGHT,
+        SDL_WINDOW_SHOWN);
+
+    if (window == nullptr) {
+        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
+        return EXIT_FAILURE;
+    } else {
+        atexit([] { SDL_DestroyWindow(window); });
+    }
+
+    renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+
+    if (renderer == nullptr) {
+        std::cerr << "SDL error: " << SDL_GetError() << std::endl;
+        return EXIT_FAILURE;
+    } else {
+        atexit([] { SDL_DestroyRenderer(renderer); });
+    }
+
+    return 0;
+}
+
+SDL_Texture *sdl2LoadTexture(const char *path)
+{
+    SDL_Texture *tex = nullptr;
+
+    auto surface = IMG_Load(path);
+    if (surface == nullptr) {
+        std::cerr << "Unable to load image " << path << '!' << std::endl;
+        std::cerr << "SDL error: " << IMG_GetError() << std::endl;
+    } else {
+        tex = SDL_CreateTextureFromSurface(renderer, surface);
+
+        if (tex == nullptr) {
+            std::cerr << "Unable to create texture for " << path << '!' << std::endl;
+            std::cerr << "SDL error: " << SDL_GetError() << std::endl;
+        } else {
+            SDL_FreeSurface(surface);
+        }
+    }
+
+    return tex;
+}
+