#pragma once
#include "imgui.h"
#include "Logger.h"
#include <limits>
#include <DirectXMath.h>
// --------------------- //
// --- Global Macros --- //
// --------------------- //
#define R_FALSE     return false;
#define R_TRUE      return true;
#define TO_RAD 0.0174532925f
#define TO_DEG (180.0f/3.141593f)

#ifdef max
    #pragma push_macro("max")
    #undef max
    #define PUSHED_MAX_DEFINED
#endif


// convert unsigned long long int to int safely
inline int ulli_to_int(unsigned long long x)
{
    return (x > static_cast<unsigned long long>(std::numeric_limits<int>::max())) ? std::numeric_limits<int>::max() : static_cast<int>(x);
}

// convert int to float safely
inline float int_to_float(int x)
{
    return (x > static_cast<int>(std::numeric_limits<float>::max())) ? std::numeric_limits<float>::max() : static_cast<float>(x);
}

// convert size_t to int safely
inline int size_t_to_int(size_t x)
{
    return (x > static_cast<size_t>(std::numeric_limits<int>::max())) ? std::numeric_limits<int>::max() : static_cast<int>(x);
}

#ifdef PUSHED_MAX_DEFINED
    #pragma pop_macro("max")
    #undef PUSHED_MAX_DEFINED
#endif
// --------------------------------------------------------- //
// --- Macros for logging with different severity levels --- //
// --------------------------------------------------------- //

#ifdef _DEBUG
#define LOG(msg, level)     Logger::Get().Log(msg, __FILE__, __LINE__, level)
#define LOG_INFO(msg)       Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Info)
#define LOG_WARNING(msg)    Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Warning)
#define LOG_ERROR(msg)      Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Error)
#define LOG_DEBUG(msg)      Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Debug)
#define LOG_SHUTDOWN(msg)   Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Shutdown)
#define LOG_INIT(msg)       Logger::Get().Log(msg, __FILE__, __LINE__, Logger::LogLevel::Initialize)

#define R_LOG_ERROR(result,msg)   \
    if(!(result)) { \
        LOG_ERROR(msg); \
        return false; \
    }

#else

#define LOG(msg, level) ((void)0)
#define LOG_INFO(msg)   ((void)0)
#define LOG_WARNING(msg)((void)0)
#define LOG_ERROR(msg)  ((void)0)
#define LOG_DEBUG(msg)  ((void)0)
#define LOG_SHUTDOWN(msg) ((void)0)
#define LOG_INIT(msg)  ((void)0)

#define R_LOG_ERROR(result,msg) ((void)0);

#endif

// ---------------------------------------------------- //
// --- Macros for the Entity Component System (ECS) --- //
// ---------------------------------------------------- //
#define CREATE_ENTITY   entity_manager_->CreateEntity();

#define SHD_ALPHA   ecs::ShaderType::ALPHA_MAPPING
#define SHD_CEL     ecs::ShaderType::CEL_SHADING
#define SHD_LIGHT   ecs::ShaderType::LIGHTING
#define SHD_NORM    ecs::ShaderType::NORMAL_MAPPING
#define SHD_REFL    ecs::ShaderType::REFLECTION
#define SHD_REFR    ecs::ShaderType::REFRACTION
#define SHD_SKYBOX  ecs::ShaderType::SKYBOX
#define SHD_SPEC    ecs::ShaderType::SPECULAR_MAPPING
#define SHD_SUN     ecs::ShaderType::SUNLIGHT
#define SHD_TEX     ecs::ShaderType::TEXTURE

// ---------------------------------------- //
// --- Macros for the application class --- //
// ---------------------------------------- //
#define CHECK_2_CONDITION_INDEPENDENT_RETURN(cond1, cond2) \
    if(!(cond1)) { \
        LOG_ERROR("Condition 1 failed: " #cond1); \
        return false; \
    } \
    if (!(cond2)) { \
        LOG_ERROR("Condition 2 failed: " #cond2); \
        return false; \
    }

#define CHECK_CONDITION_RETURN(cond) \
    if(!(cond)) { \
        LOG_ERROR("Condition failed: " #cond); \
        return false; \
    }

// ------------------------------------------------ //
// --- Macros and constexpr for the ImGUI class --- //
// ------------------------------------------------ //
constexpr ImVec4 BG_DK_GRAY     = ImVec4(0.1f, 0.1f, 0.1f, 1.0f);
constexpr ImVec4 BG_GRAY        = ImVec4(0.2f, 0.2f, 0.2f, 1.0f);
constexpr ImVec4 BG_LT_GRAY     = ImVec4(0.3f, 0.3f, 0.3f, 1.0f);
constexpr ImVec4 ACCENT_COLOR   = ImVec4(0.14f, 0.5f, 0.8f, 0.5f);
constexpr ImVec4 ACCENT_LT_COLOR= ImVec4(0.14f, 0.5f, 0.8f, 1.0f);
constexpr ImVec4 TEXT_COLOR     = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
constexpr ImVec4 TEXT_DIM_COLOR = ImVec4(0.6f, 0.6f, 0.6f, 1.0f);
constexpr ImVec4 BORDER_COLOR   = ImVec4(0.25f, 0.25f, 0.27f, 1.00f);
constexpr ImVec4 BLK            = ImVec4(0.0f, 0.0f, 0.0f, 1.0f);

#define BASIC_DOCK_STYLE_1 \
    ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); \
    ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f); \
    ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); \
    ImGui::PushStyleVar(ImGuiStyleVar_DockingSeparatorSize, 1.0f);

#define BEGIN(name, p_open)     ImGui::Begin(name, p_open);
#define S_LINE                  ImGui::SameLine();
#define E_MENU                  ImGui::EndMenu();
#define END                     ImGui::End();
#define TEXT_V(label,var)       ImGui::Text(label, var);
#define SEP                     ImGui::Separator();
#define PSV                     ImGui::PopStyleVar();
#define O_POPUP(str)            ImGui::OpenPopup(str);
#define E_POPUP                 ImGui::EndPopup();
#define TEX(label)              ImGui::Text(label);

#define BUTTON(label)                       ImGui::Button(label)
#define B_MENU(label)                       ImGui::BeginMenu(label)
#define M_ITEM_LP(label, p_open)            ImGui::MenuItem(label, NULL, p_open)
#define M_ITEM_L(label)                     ImGui::MenuItem(label)
#define F_SLIDER(label, value, min, max)    ImGui::SliderFloat(label, value, min, max)
#define I_SLIDER(label, value, min, max)    ImGui::SliderInt(label, value, min, max)
#define C_HEADER(label)                     ImGui::CollapsingHeader(label)
#define SEL(label, isSelected)              ImGui::Selectable(label, isSelected)
#define I_TEX(label, buf, buf_size)         ImGui::InputText(label, buf, buf_size)
#define C_BOX(label, v)                     ImGui::Checkbox(label, v)
#define B_POPUP(str)                        ImGui::BeginPopup(str)

// ------------------------------------ //
// --- Macros for the frustum class --- //
// ------------------------------------ //
inline bool TestPlaneCorner(
    const DirectX::XMVECTOR& plane,
    float xPos, float yPos, float zPos,
    float tolerance)
{
    float val =
        DirectX::XMVectorGetX(plane) * xPos +
        DirectX::XMVectorGetY(plane) * yPos +
        DirectX::XMVectorGetZ(plane) * zPos +
        DirectX::XMVectorGetW(plane);
    return val > -tolerance;
}

#define CHECK_FRUSTRUM_CUBE_CORNERS(plane, xCenter, yCenter, zCenter, radius, tolerance) \
    do { \
        const float offsets[2] = { -radius, radius }; \
        bool inside = false; \
        for (int xi = 0; xi < 2 && !inside; ++xi) { \
            for (int yi = 0; yi < 2 && !inside; ++yi) { \
                for (int zi = 0; zi < 2 && !inside; ++zi) { \
                    if (TestPlaneCorner(plane, xCenter + offsets[xi], yCenter + offsets[yi], zCenter + offsets[zi], tolerance)) { \
                        inside = true; \
                    } \
                } \
            } \
        } \
        if (!inside) { \
            R_FALSE; \
        } \
    } while (0);

// ---------------------------------------------- //
// --- Macros for the camera input processing --- //
// ---------------------------------------------- //
struct CameraInput {
    bool move_forward = false;
    bool move_backward = false;
    bool move_left = false;
    bool move_right = false;
    bool move_up = false;
    bool move_down = false;
    bool scroll_up = false;
    bool scroll_down = false;
    bool right_click = false;
};