#pragma once
#include <Windows.h>

#include <functional>

#define GLFW_INCLUDE_NONE
#define GLFW_INCLUDE_VULKAN
#define GLFW_EXPOSE_NATIVE_WIN32
#include <GLFW/glfw3.h>

#include "imgui.h"
#include "remixicon.h"

#define IMGUI_REDRAW_COUNT 3

class DrawData {
public:
    GLFWwindow* window = nullptr;
    HWND window_handle = nullptr;
    ImVec4 clear_color = {};
    ImVec2 window_size = {};
    float frameTimeSetup = 0.f;
    float frameTimeDraw = 0.f;
    float frameTimeRender = 0.f;
    float frameTimeDisplay = 0.f;
};

class ImGuiCallbacks {
public:
    std::function<void(DrawData&)> initFunc = nullptr;
    std::function<void(DrawData&)> drawFunc = nullptr;
    std::function<void(DrawData&)> cleanupFunc = nullptr;
};

class PerfTimer {
private:
    LARGE_INTEGER start;
    LARGE_INTEGER end;
    LARGE_INTEGER freq;

public:
    LONGLONG resultTicks;
    float resultSeconds;

public:
    void Start() {
        QueryPerformanceFrequency(&freq);
        QueryPerformanceCounter(&start);
    }
    void End() {
        QueryPerformanceCounter(&end);
        resultTicks = end.QuadPart - start.QuadPart;
        resultSeconds = static_cast<float>(resultTicks * 1000) / freq.QuadPart;
    }
};

int startImgui(ImGuiCallbacks& callbacks, const char* title, int windowWidth, int windowHeight);

ImVec2 getWindowSize(GLFWwindow* window);

constexpr float DEFAULT_DROP_SHADOW_SIZE = 3.f;
constexpr ImGuiCol DEFAULT_DROP_SHADOW_COLOR_ID = ImGuiCol_TabUnfocused;

void dropShadow();
void dropShadow(const float shadowSize, const ImColor& color);
void renderDropShadow(const float shadowSize, const ImVec2& min, const ImVec2& max, const ImColor& color);

bool dropButton(const char* label, const ImVec2& size = ImVec2(0, 0), float shadowSize = DEFAULT_DROP_SHADOW_SIZE, bool smallButton = false);
bool checkboxWithDropShadow(const char* label, bool* v, const float shadowSize = DEFAULT_DROP_SHADOW_SIZE);

template <typename T>
float calcInputWidth(const char* formatStr, T value)
{
    constexpr size_t MAX_BUFFER_SIZE = 256;
    char formatStrBuffer[MAX_BUFFER_SIZE];
    snprintf(formatStrBuffer, MAX_BUFFER_SIZE, formatStr, value);
    return ImGui::CalcTextSize(formatStrBuffer).x + ImGui::GetStyle().FramePadding.x * 2.f;
}

ImVec2 operator +(const ImVec2& a, const ImVec2& b);
ImVec2 operator +(const ImVec2& a, const float s);
ImVec2 operator -(const ImVec2& a, const ImVec2& b);
ImVec2 operator -(const ImVec2& a, const float s);
ImVec2 operator *(const ImVec2& a, float s);
ImVec2 operator /(const ImVec2& a, float s);
