#define GLFW_INCLUDE_NONE
#include <GLFW/glfw3.h>
#include "glad.h"

#include "Renderer.h"
#include "Win32AssetManager.h"
#include "TouchInput.h"
#include "Position.h"
#include "GameState.h"
#include "Profiling.h"

#undef APIENTRY
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include <assert.h>

struct CodePointsArray
{
    unsigned int* cps; // codepoints
    unsigned int count;
    unsigned int cap;
};

struct UserData
{
    CodePointsArray cp_array;
    double scroll_offset;
};

static void errorCallback(int error, const char* desc)
{
    MessageBoxA(NULL, desc, "Error", MB_ICONERROR | MB_OK);
}

static void charCallback(GLFWwindow* window, unsigned int cp)
{
    ZoneScoped;

    UserData* ud = reinterpret_cast<UserData*>(glfwGetWindowUserPointer(window));
    assert(ud != nullptr);
    if (ud->cp_array.count < ud->cp_array.cap)
        ud->cp_array.cps[ud->cp_array.count++] = cp;
}

static void scrollCallback(GLFWwindow* window, double /* xoffset */, double yoffset)
{
    ZoneScoped;

    UserData* ud = reinterpret_cast<UserData*>(glfwGetWindowUserPointer(window));
    assert(ud != nullptr);
    ud->scroll_offset = yoffset;
}

int main()
{
    glfwSetErrorCallback(errorCallback);

    if (!glfwInit()) {
        return 1;
    }

    unsigned int pressed_codepoints[100];
    UserData ud = {};
    ud.cp_array = {pressed_codepoints, 0, 100};

    glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
    GLFWwindow* window = glfwCreateWindow(1024, 768, "Krimi Dinner", nullptr, nullptr);
    if (!window) {
        glfwTerminate();
        return 1;
    }
    glfwSetWindowUserPointer(window, &ud);
    glfwSetCharCallback(window, charCallback);
    glfwSetScrollCallback(window, scrollCallback);

    glfwMakeContextCurrent(window);
    int ret = gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress);
    if (!ret) {
        MessageBoxA(nullptr, "Failed to load GLES3", "Error", MB_ICONERROR | MB_OK);
        glfwDestroyWindow(window);
        glfwTerminate();
        return 1;
    }

    GameState state = {};

    const char* asset_dir = "app\\src\\main\\assets";
    Win32AssetManager::create(asset_dir);

    Renderer::create();

    // BEISPIELCODE
    Position smiley_pos = {512, 384};
    StringHandle smiley = StringRepository::global->internString("smiley_PNG42.png");
    StringHandle ttf = StringRepository::global->internString("Milky Honey.ttf");
    FontData fd;
    AssetManager::ptr->loadFontData("Milky Honey.ttf", 128.f, &fd);
    // ENDE BEISPIELCODE

    glfwSwapInterval(1);
    while (!glfwWindowShouldClose(window)) {
        /* Reset the pressed codepoints array and scroll offset
         * because glfwPollEvents() will call our callbacks
         */
        ud.cp_array.count = 0;
        ud.scroll_offset = 0.0;
        glfwPollEvents();

        /* Gather keyboard input */
        int pressed_keys[GLFW_KEY_LAST + 1];
        unsigned int pressed_key_count = 0;
        for (int key = GLFW_KEY_SPACE; key <= GLFW_KEY_LAST; ++key) {
            if (glfwGetKey(window, key) == GLFW_PRESS) {
                pressed_keys[pressed_key_count++] = key;
            }
        }

        /* Gather mouse input */
        TouchInputEvent input_events[1] = {};
        int input_event_count = 0;
        if (glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_LEFT) == GLFW_PRESS) {
            double x, y;
            glfwGetCursorPos(window, &x, &y);
            input_events[0].kind = TouchInputEventKind::Tap;
            input_events[0].start = {static_cast<float>(x), static_cast<float>(y)};
            input_events[0].end = input_events[0].start;
            input_event_count = 1;
        }

        int w, h;
        glfwGetFramebufferSize(window, &w, &h);

        float display_width = static_cast<float>(w);
        float display_height = static_cast<float>(h);

        // INTEGRATION Rufe hier deine "gameloop"/"update" funktion auf, die als Parameter
        // den GameState, input_events und input_event_count und die Displaygröße bekommen sollte:
        //
        // kUpdate(&state, input_events, input_event_count,
        //         ud.scroll_offset,
        //         ud.cp_array.cps, ud.cp_array.count,
        //         pressed_keys, pressed_key_count,
        //         display_width, display_height)
        //
        //
        // Die Funktion könnte folgende Definition haben:
        // void kUpdate(GameState* state,
        //              const TouchInputEvent* touch_events,
        //              unsigned int touch_event_count,
        //              double scroll_offset,
        //              const unsigned int* pressed_codepoints,
        //              unsigned int codepoint_count,
        //              const int* pressed_keys,
        //              unsigned int pressed_key_count,
        //              float display_width,
        //              float display_height)
        // ENDE INTEGRATION

        // BEISPIELCODE
        static float x = 1.f;
        static float d = -0.01f;
        x += d;
        if (x <= 0.f)
            d *= -1.f;
        else if (x >= 1.f)
            d *= -1.f;

        if (input_event_count > 0) {
            smiley_pos = input_events[0].end;
            smiley_pos.x -= 250;
            smiley_pos.y -= 250;
        }
        if (ud.scroll_offset != 0) {
            smiley_pos.y += ud.scroll_offset;
        }

        if (pressed_key_count > 0) {
            for (unsigned int i = 0; i < pressed_key_count; ++i) {
                if (pressed_keys[i] == GLFW_KEY_LEFT_CONTROL)
                    smiley_pos = {100, 100};
            }
        }

        Renderer::ptr->addRect(100, 100, 500, 500, 0.3f, 0.3f, 0.3f, 1.f);
        Renderer::ptr->addRect(smiley_pos.x, smiley_pos.y, 500, 500, 0.f, x * x, 1.f - x * x, 1.f, smiley);

        const char* text = "Hello, Sailor!";
        float tx = 100, ty = 200;
        while (*text) {
            if (*text >= 32 && *text < 128) {
                stbtt_aligned_quad q;
                stbtt_GetPackedQuad(fd.char_data, fd.bitmap_size, fd.bitmap_size, *text - 32, &tx, &ty, &q, 1);
                float w = q.x1 - q.x0, h = q.y1 - q.y0;
                Renderer::ptr->addFontRect(q.x0,
                                           q.y0,
                                           w,
                                           h,
                                           1.f, 0.f, 0.f, 1.f, 
                                           fd.char_height,
                                           ttf,
                                           StringRepository::global->internString("1"),
                                           q.s0,
                                           q.t0,
                                           q.s1,
                                           q.t1);
            }
            ++text;
        }

        // ENDE BEISPIELCODE

        Renderer::ptr->renderFrame(static_cast<float>(w), static_cast<float>(h));

        glfwSwapBuffers(window);
        FrameMark;
    }

    glfwDestroyWindow(window);
    glfwTerminate();
    return 0;
}