#pragma once

#include <Core/Common/CoreTypes.h>
#include <Juliet.h>

#include <algorithm>
#include <bit>
#include <cassert>
#include <concepts>
#include <source_location>

namespace Juliet
{

#define global static

    // 1. Stringify helpers
#define JULIET_STR(x) #x
#define JULIET_TOSTRING(x) JULIET_STR(x)

    // 2. Define the pragma operator based on compiler
#if defined(__clang__) || defined(__GNUC__)
#define JULIET_PRAGMA(x) _Pragma(#x)
#define JULIET_SUPPRESS_MSVC(id)
#define JULIET_SUPPRESS_CLANG(str) JULIET_PRAGMA(clang diagnostic ignored str)
#elif defined(_MSC_VER)
#define JULIET_PRAGMA(x) __pragma(x)
#define JULIET_SUPPRESS_MSVC(id) JULIET_PRAGMA(warning(disable : id))
#define JULIET_SUPPRESS_CLANG(str)
#else
#define JULIET_PRAGMA(x)
#define JULIET_SUPPRESS_MSVC(id)
#define JULIET_SUPPRESS_CLANG(str)
#endif

    // 3. The Agnostic "Push/Pop"
#if defined(__clang__)
#define JULIET_WARNING_PUSH JULIET_PRAGMA(clang diagnostic push)
#define JULIET_WARNING_POP JULIET_PRAGMA(clang diagnostic pop)
#elif defined(_MSC_VER)
#define JULIET_WARNING_PUSH JULIET_PRAGMA(warning(push))
#define JULIET_WARNING_POP JULIET_PRAGMA(warning(pop))
#else
#define JULIET_WARNING_PUSH
#define JULIET_WARNING_POP
#endif

#if defined(_MSC_VER)
    // MSVC specific intrinsic
#define JULIET_PLATFORM_BREAK() (__nop(), __debugbreak())
#elif defined(__clang__) || defined(__GNUC__)
    // Clang/GCC specific intrinsic
#define JULIET_PLATFORM_BREAK() __builtin_trap()
#else
#include <signal.h>
#define JULIET_PLATFORM_BREAK() raise(SIGTRAP)
#endif

#if JULIET_DEBUG
#define JULIET_ASSERT_INTERNAL(expression, message)                                                                    \
    JULIET_WARNING_PUSH                                                                                                \
    JULIET_SUPPRESS_CLANG("-Wextra-semi-stmt")                                                                         \
    JULIET_SUPPRESS_MSVC(4127)                                                                                         \
    JULIET_SUPPRESS_MSVC(4548)                                                                                         \
    {                                                                                                                  \
        if (!(expression)) [[unlikely]]                                                                                \
        {                                                                                                              \
            Juliet::JulietAssert(#expression, message);                                                                \
        }                                                                                                              \
    }                                                                                                                  \
    JULIET_WARNING_POP                                                                                                 \
    static_assert(true, "")

#define AssertHR(hr_expression, message)                                                                               \
    do                                                                                                                 \
    {                                                                                                                  \
        long hr_val = (hr_expression);                                                                                 \
        if (hr_val < 0)                                                                                                \
        {                                                                                                              \
            Juliet::JulietAssert(#hr_expression, message, std::source_location::current(), hr_val);                    \
        }                                                                                                              \
    }                                                                                                                  \
    while (0)

#define GET_ASSERT_MACRO(_1, _2, NAME, ...) NAME
#define Assert(...) GET_ASSERT_MACRO(__VA_ARGS__, JULIET_ASSERT_INTERNAL, JULIET_ASSERT_NO_MSG)(__VA_ARGS__)
#define JULIET_ASSERT_NO_MSG(expression) JULIET_ASSERT_INTERNAL(expression, "No additional information provided.")

#define Unimplemented()                                                                                                \
    do                                                                                                                 \
    {                                                                                                                  \
        Juliet::JulietAssert("UNIMPLEMENTED", "This code path is not yet functional.");                                \
    }                                                                                                                  \
    while (0)

#else
#define Assert(...) ((void)0)
#define AssertHR(hr_expression, message) ((void)(hr_expression))
#define Unimplemented() ((void)0)
#endif

    JULIET_API extern void JulietAssert(const char* expression, const char* message,
                                        std::source_location location = std::source_location::current(), long handleResult = 0);

#define ZeroStruct(structInstance) ZeroSize(sizeof(structInstance), &(structInstance))
#define ZeroArray(array) ZeroSize(sizeof((array)), (array))
#define ZeroDynArray(Count, Pointer) ZeroSize((Count) * sizeof((Pointer)[0]), Pointer)
    inline void ZeroSize(size_t size, void* ptr)
    {
        auto Byte = (uint8*)ptr;
        while (size--)
        {
            *Byte++ = 0;
        }
    }

#define Restrict __restrict

    template <class Function>
    class DeferredFunction
    {
      public:
        explicit DeferredFunction(const Function& otherFct) noexcept
          : Callback(otherFct)
        {
        }
        explicit DeferredFunction(Function&& otherFct) noexcept
          : Callback(std::move(otherFct))
        {
        }

        ~DeferredFunction() noexcept { Callback(); }

        DeferredFunction(const DeferredFunction&)  = delete;
        DeferredFunction(const DeferredFunction&&) = delete;
        void operator=(const DeferredFunction&)    = delete;
        void operator=(DeferredFunction&&)         = delete;

      private:
        Function Callback;
    };

    template <class Function>
    auto Defer(Function&& fct) noexcept
    {
        return DeferredFunction<std::decay_t<Function>>{ std::forward<Function>(fct) };
    }

    inline bool IsValid(ByteBuffer buffer)
    {
        return buffer.Size > 0 && buffer.Data;
    }

    extern JULIET_API void Free(ByteBuffer& buffer);

    template <std::integral T>
    [[nodiscard]] constexpr T AlignPow2(T x, T alignment)
    {
        // Safety Check:
        Assert(std::has_single_bit(static_cast<size_t>(alignment)));

        return (x + alignment - 1) & ~(alignment - 1);
    }

    template <typename T>
    inline void Swap(T* Restrict a, T* Restrict b)
    {
        T temp = std::move(*a);
        *a     = std::move(*b);
        *b     = std::move(temp);
    }

    // Move to another file dedicated to those
#if defined(__clang__)
#define COMPILER_CLANG 1
#elif defined(_MSC_VER)
#define COMPILER_MSVC 1
#endif

    // Undef anything not defined
#if !defined(COMPILER_CLANG)
#define COMPILER_CLANG 0
#endif
#if !defined(COMPILER_MSVC)
#define COMPILER_MSVC 0
#endif

#if COMPILER_MSVC
#define AlignOf(T) __alignof(T)
#elif COMPILER_CLANG
#define AlignOf(T) __alignof(T)
#elif COMPILER_GCC
#define AlignOf(T) __alignof__(T)
#else
#error AlignOf not defined for this compiler.
#endif

    template <typename T>
    [[nodiscard]] constexpr const char* GetTypeName()
    {
#if COMPILER_CLANG
        return __PRETTY_FUNCTION__;
#elif COMPILER_MSVC
        return __FUNCSIG__;
#elif COMPILER_GCC
        return __PRETTY_FUNCTION__;
#else
        return "UnknownType";
#endif
    }
} // namespace Juliet
