/****************************************************************************
* Copyright (C) 2014-2015 Intel Corporation.   All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
****************************************************************************/

#ifndef __SWR_ASSERT_H__
#define __SWR_ASSERT_H__

#if !defined(__SWR_OS_H__)
#error swr_assert.h should not be included directly, please include "common/os.h" instead.
#endif

//=============================================================================
//
// MACROS defined in this file:
//
// - SWR_ASSUME(expression, ...):   Tell compiler that the expression is true.
//                                  Helps with static code analysis as well.
//                                  DO NOT USE if code after this dynamically
//                                  checks for errors and handles them.  The
//                                  compiler may optimize out the error check.
//
// - SWR_ASSERT(expression, ...):   Inform the user is expression is false.
//                                  This check is only conditionally made,
//                                  usually only in debug mode.
//
// - SWR_REL_ASSERT(expression, ...): Unconditionally enabled version of SWR_ASSERT
//
// - SWR_ASSUME_ASSERT(expression, ...): Conditionally enabled SWR_ASSERT.  Uses
//                                       SWR_ASSUME if SWR_ASSERT is disabled.
//                                       DO NOT USE in combination with actual
//                                       error checking (see SWR_ASSUME)
//
// - SWR_REL_ASSUME_ASSERT(expression, ...): Same as SWR_REL_ASSERT.
//
//=============================================================================

// Stupid preprocessor tricks to avoid -Wall / -W4 warnings
#if defined(_MSC_VER)
#define _SWR_WARN_DISABLE __pragma(warning(push)) __pragma(warning(disable:4127))
#define _SWR_WARN_RESTORE __pragma(warning(pop))
#else // ! MSVC compiler
#define _SWR_WARN_DISABLE
#define _SWR_WARN_RESTORE
#endif

#define _SWR_MACRO_START do {
#define _SWR_MACRO_END  \
    _SWR_WARN_DISABLE   \
    } while(0)          \
    _SWR_WARN_RESTORE


#if defined(_WIN32)
#define SWR_ASSUME(e, ...) _SWR_MACRO_START __assume(e); _SWR_MACRO_END
#elif defined(__clang__)
#define SWR_ASSUME(e, ...) _SWR_MACRO_START __builtin_assume(e); _SWR_MACRO_END
#elif defined(__GNUC__)
#define SWR_ASSUME(e, ...) _SWR_MACRO_START ((e) ? ((void)0) : __builtin_unreachable()); _SWR_MACRO_END
#else
#define SWR_ASSUME(e, ...) _SWR_MACRO_START ASSUME(e); _SWR_MACRO_END
#endif

#if !defined(SWR_ENABLE_ASSERTS)

#if !defined(NDEBUG)
#define SWR_ENABLE_ASSERTS 1
#else
#define SWR_ENABLE_ASSERTS 0
#endif // _DEBUG

#endif // SWR_ENABLE_ASSERTS

#if !defined(SWR_ENABLE_REL_ASSERTS)
#define SWR_ENABLE_REL_ASSERTS 1
#endif

#if SWR_ENABLE_ASSERTS || SWR_ENABLE_REL_ASSERTS
#include "assert.h"

#if !defined(__cplusplus)

#pragma message("C++ is required for SWR Asserts, falling back to assert.h")

#if SWR_ENABLE_ASSERTS
#define SWR_ASSERT(e, ...) assert(e)
#endif

#if SWR_ENABLE_REL_ASSERTS
#define SWR_REL_ASSERT(e, ...) assert(e)
#endif

#else

bool SwrAssert(
    bool        chkDebugger,
    bool&       enabled,
    const char* pExpression,
    const char* pFileName,
    uint32_t    lineNum,
    const char* function,
    const char* pFmtString = nullptr,
    ...);

void SwrTrace(
    const char* pFileName,
    uint32_t    lineNum,
    const char* function,
    const char* pFmtString,
    ...);

#define _SWR_ASSERT(chkDebugger, e, ...)    \
    _SWR_MACRO_START \
    bool expFailed = !(e);\
    if (expFailed) {\
        static bool swrAssertEnabled = true;\
        expFailed = SwrAssert(chkDebugger, swrAssertEnabled, #e, __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);\
        if (expFailed) { DEBUGBREAK; }\
    }\
    _SWR_MACRO_END

#define _SWR_INVALID(chkDebugger, ...)    \
    _SWR_MACRO_START \
    static bool swrAssertEnabled = true;\
    bool expFailed = SwrAssert(chkDebugger, swrAssertEnabled, "", __FILE__, __LINE__, __FUNCTION__, ##__VA_ARGS__);\
    if (expFailed) { DEBUGBREAK; }\
    _SWR_MACRO_END

#define _SWR_TRACE(_fmtstr, ...) \
    SwrTrace(__FILE__, __LINE__, __FUNCTION__, _fmtstr, ##__VA_ARGS__);

#if SWR_ENABLE_ASSERTS
#define SWR_ASSERT(e, ...)              _SWR_ASSERT(true, e, ##__VA_ARGS__)
#define SWR_ASSUME_ASSERT(e, ...)       SWR_ASSERT(e, ##__VA_ARGS__)
#define SWR_TRACE(_fmtstr, ...)         _SWR_TRACE(_fmtstr, ##__VA_ARGS__)

#if defined(assert)
#undef assert
#endif
#define assert(exp) SWR_ASSERT(exp)

#endif // SWR_ENABLE_ASSERTS

#if SWR_ENABLE_REL_ASSERTS
#define SWR_REL_ASSERT(e, ...)          _SWR_ASSERT(false, e, ##__VA_ARGS__)
#define SWR_REL_ASSUME_ASSERT(e, ...)   SWR_REL_ASSERT(e, ##__VA_ARGS__)
#define SWR_REL_TRACE(_fmtstr, ...)     _SWR_TRACE(_fmtstr, ##__VA_ARGS__)

// SWR_INVALID is always enabled
// Funky handling to allow 0 arguments with g++/gcc
// This is needed because you can't "swallow commas" with ##_VA_ARGS__ unless
// there is a first argument to the macro.  So having a macro that can optionally
// accept 0 arguments is tricky.
#define _SWR_INVALID_0()                _SWR_INVALID(false)
#define _SWR_INVALID_1(...)             _SWR_INVALID(false, ##__VA_ARGS__)
#define _SWR_INVALID_VARGS_(_10, _9, _8, _7, _6, _5, _4, _3, _2, _1, N, ...) N
#define _SWR_INVALID_VARGS(...)         _SWR_INVALID_VARGS_(__VA_ARGS__, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1)
#define _SWR_INVALID_VARGS_0()          1, 2, 3, 4, 5, 6, 7, 9, 9, 10
#define _SWR_INVALID_CONCAT_(a, b)      a##b
#define _SWR_INVALID_CONCAT(a, b)       _SWR_INVALID_CONCAT_(a, b)
#define SWR_INVALID(...)                \
    _SWR_INVALID_CONCAT(_SWR_INVALID_,_SWR_INVALID_VARGS(_SWR_INVALID_VARGS_0 __VA_ARGS__ ()))(__VA_ARGS__)
#endif

#endif // C++

#endif // SWR_ENABLE_ASSERTS || SWR_ENABLE_REL_ASSERTS

// Needed to allow passing bitfield members to sizeof() in disabled asserts
template<typename T>
static bool SwrSizeofWorkaround(T) {return false;}

#if !SWR_ENABLE_ASSERTS
#define SWR_ASSERT(e, ...)              _SWR_MACRO_START (void)sizeof(SwrSizeofWorkaround(e)); _SWR_MACRO_END
#define SWR_ASSUME_ASSERT(e, ...)       SWR_ASSUME(e, ##__VA_ARGS__)
#define SWR_TRACE(_fmtstr, ...)         _SWR_MACRO_START (void)(0); _SWR_MACRO_END
#endif

#if !SWR_ENABLE_REL_ASSERTS
#define SWR_REL_ASSERT(e, ...)          _SWR_MACRO_START (void)sizeof(SwrSizeofWorkaround(e)); _SWR_MACRO_END
#define SWR_INVALID(...)                _SWR_MACRO_START (void)(0); _SWR_MACRO_END
#define SWR_REL_ASSUME_ASSERT(e, ...)   SWR_ASSUME(e, ##__VA_ARGS__)
#define SWR_REL_TRACE(_fmtstr, ...)     _SWR_MACRO_START (void)(0); _SWR_MACRO_END
#endif

#if defined(_MSC_VER)
#define SWR_FUNCTION_DECL __FUNCSIG__
#elif (defined(__GNUC__) || defined(__clang__))
#define SWR_FUNCTION_DECL __PRETTY_FUNCTION__
#else
#define SWR_FUNCTION_DECL __FUNCTION__
#endif

#define SWR_NOT_IMPL SWR_INVALID("%s not implemented", SWR_FUNCTION_DECL)

#endif//__SWR_ASSERT_H__
