//===-- rosa/support/debug.hpp ----------------------------------*- C++ -*-===//
//
//                                 The RoSA Framework
//
// Distributed under the terms and conditions of the Boost Software License 1.0.
// See accompanying file LICENSE.
//
// If you did not receive a copy of the license file, see
// http://www.boost.org/LICENSE_1_0.txt.
//
//===----------------------------------------------------------------------===//
///
/// \file rosa/support/debug.hpp
///
/// \author David Juhasz (david.juhasz@tuwien.ac.at)
///
/// \date 2017-2019
///
/// \brief Facility for debugging
///
//===----------------------------------------------------------------------===//

#ifndef ROSA_SUPPORT_DEBUG_HPP
#define ROSA_SUPPORT_DEBUG_HPP

#include "rosa/config/config.h"

#include "rosa/support/terminal_colors.h"
#include "rosa/support/type_helper.hpp"

#include <array>
#include <ostream>

namespace rosa {

/// Returns an output stream to use for debugging.
std::ostream &dbgs(void);

/// Prints a \c std::array to a \c std::ostream.
///
/// \tparam T type of values stored in \p A
/// \tparam Size number of elements in \p A
///
/// \param [in,out] OS \c std::ostream to print to
/// \param A \c std::array to print to \p OS
///
/// \return \p OS after printing \p A
template <typename T, size_t Size>
std::ostream &operator<<(std::ostream &OS, const std::array<T, Size> &A) {
  OS << '[';
  for (unsigned I = 0; I < Size; ++I) {
    if (I) {
      OS << ',';
    }
    OS << PRINTABLE(A[I]);
  }
  OS << ']';
  return OS;
}

} // End namespace rosa

/// \def ASSERT(stmt)
/// \brief Enforces an assertion.
///
/// \note Takes effect only when \c ROSA_ENABLE_ASSERTIONS is defined.
///
/// Checks if \p stmt evaluates to true. If not, prints an error message about
/// violating the assertion and aborts execution.
///
/// \param stmt statement to evaluate, needs to be convertable to \c bool

#ifndef ROSA_ENABLE_ASSERTIONS
#define ASSERT(stmt) ROSA_IGNORE_UNUSED_EXPR(stmt)
#elif defined(ROSA_WINDOWS)
#define ASSERT(stmt)                                                           \
  if (static_cast<bool>(stmt) == false) {                                      \
    rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":"      \
                 << __LINE__ << ": requirement failed: '" << #stmt << "'"      \
                 << std::endl;                                                 \
    ::abort();                                                                 \
  }                                                                            \
  ROSA_VOID_STMT
#else // defined(ROSA_LINUX)
#include <execinfo.h>
#define ASSERT(stmt)                                                           \
  if (static_cast<bool>(stmt) == false) {                                      \
    rosa::dbgs() << rosa::terminal::Color::Default << __FILENAME__ << ":"      \
                 << __LINE__ << ": requirement failed: '" << #stmt << "'"      \
                 << std::endl;                                                 \
    void *array[20];                                                           \
    auto bt_size = ::backtrace(array, 20);                                     \
    ::backtrace_symbols_fd(array, bt_size, 2);                                 \
    ::abort();                                                                 \
  }                                                                            \
  ROSA_VOID_STMT
#endif // defined(ROSA_ENABLE_ASSERTIONS)

/// \def DEBUG(X)
/// \brief Executes the given piece of code only if \c NDEBUG is not defined.
///
/// \param X the code to execute

/// \def DEBUGVAR(V)
/// \brief Dumps the given variable to the default debug output.
///
/// \param V the variable to dump
///
/// \sa \c rosa::dbgs()

#ifndef NDEBUG
#define DEBUG(X)                                                               \
  do {                                                                         \
    X;                                                                         \
  } while (false)
#define DEBUGVAR(V)                                                            \
  do {                                                                         \
    rosa::dbgs() << rosa::terminal::Color::Default << #V << " ("               \
                 << __FILENAME__ << ":" << __LINE__ << "): " << (V)            \
                 << std::endl;                                                 \
  } while (false)
#else // defined(NDEBUG)
#define DEBUG(X) ROSA_IGNORE_UNUSED_STMT(X)
#define DEBUGVAR(X) ROSA_IGNORE_UNUSED_EXPR(X)
#endif // defined(NDEBUG)

/// Enforces static assertion.
///
/// \param COND the condition to evaluate, must be statically evaluable
/// \param DIAG error message if \p COND does not evaluate to \c true
#define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG)

#endif // ROSA_SUPPORT_DEBUG_HPP
