#pragma once
#include <luna/SourceLocation.h>

[[noreturn]] extern bool __check_failed(SourceLocation location, const char* expr);

#ifndef STRINGIZE_VALUE_OF
#define STRINGIZE(x) #x
#define STRINGIZE_VALUE_OF(x) STRINGIZE(x)
#endif

// Like check(), but with a custom error message.
#define expect(expr, message)                                                                                          \
    do {                                                                                                               \
        if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), message); }                              \
    } while (0)

#define expect_at(expr, location, message)                                                                             \
    do {                                                                                                               \
        if (!(expr)) [[unlikely]] { __check_failed(location, message); }                                               \
    } while (0)

// Fail with an error message and location.
#define fail(message) __check_failed(SourceLocation::current(), message)
#define fail_at(location, message) __check_failed(location, message)

// Like assert(), but always enabled.
#define check(expr)                                                                                                    \
    do {                                                                                                               \
        if (!(expr)) [[unlikely]] { __check_failed(SourceLocation::current(), #expr); }                                \
    } while (0)

#define check_at(expr, location)                                                                                       \
    do {                                                                                                               \
        if (!(expr)) [[unlikely]] { __check_failed(location, #expr); }                                                 \
    } while (0)

#define unreachable() __check_failed(SourceLocation::current(), "Reached unreachable code")

#define todo() __check_failed(SourceLocation::current(), "Reached a TODO!")