// This file is part of Desktop App Toolkit,
// a set of libraries for developing nice desktop applications.
//
// For license and copyright information please follow this link:
// https://github.com/desktop-app/legal/blob/master/LEGAL
//
#pragma once

#include <cstdlib>

// Ensures/Expects.
#include <gsl/gsl_assert>

namespace base {
namespace assertion {

void log(const char *message, const char *file, int line);

// Release build assertions.
inline constexpr void noop() {
}

[[noreturn]] inline void fail(
		const char *message,
		const char *file,
		int line) {
	log(message, file, line);

	// Crash with access violation and generate crash report.
	volatile auto nullptr_value = (int*)nullptr;
	*nullptr_value = 0;

	// Silent the possible failure to comply noreturn warning.
	std::abort();
}

constexpr const char* extract_basename(const char* path, size_t size) {
	while (size != 0 && path[size - 1] != '/' && path[size - 1] != '\\') {
		--size;
	}
	return path + size;
}

} // namespace assertion
} // namespace base

#if defined(__clang__) || defined(__GNUC__)
#define AssertUnlikelyHelper(x) __builtin_expect(!!(x), 0)
#else
#define AssertUnlikelyHelper(x) (!!(x))
#endif

#define AssertValidationCondition(condition, message, file, line)\
	((AssertUnlikelyHelper(!(condition)))\
		? ::base::assertion::fail(message, file, line)\
		: ::base::assertion::noop())

#define SOURCE_FILE_BASENAME (::base::assertion::extract_basename(\
	__FILE__,\
	sizeof(__FILE__)))

#define AssertCustom(condition, message) (AssertValidationCondition(\
	condition,\
	message,\
	SOURCE_FILE_BASENAME,\
	__LINE__))
#define Assert(condition) AssertCustom(condition, "\"" #condition "\"")

// Define our own versions of Expects() and Ensures().
// Let them crash with reports and logging.
#ifdef Expects
#undef Expects
#endif // Expects
#define Expects(condition) (AssertValidationCondition(\
	condition,\
	"\"" #condition "\"",\
	SOURCE_FILE_BASENAME,\
	__LINE__))

#ifdef Ensures
#undef Ensures
#endif // Ensures
#define Ensures(condition) (AssertValidationCondition(\
	condition,\
	"\"" #condition "\"",\
	SOURCE_FILE_BASENAME,\
	__LINE__))

#ifdef Unexpected
#undef Unexpected
#endif // Unexpected
#define Unexpected(message) (::base::assertion::fail(\
	"Unexpected: " message,\
	SOURCE_FILE_BASENAME,\
	__LINE__))

#ifdef _DEBUG
#define AssertIsDebug(...)
#endif // _DEBUG
