/*******************************************************************************
 *
 * File:     debug.hpp
 *
 * Contents: Facility for debugging
 *
 * Copyright 2017
 *
 * Author: David Juhasz (david.juhasz@tuwien.ac.at)
 *
 ******************************************************************************/

#ifndef ROSA_SUPPORT_DEBUG_HPP
#define ROSA_SUPPORT_DEBUG_HPP

#include "rosa/config/config.h"
#include "rosa/support/type_helper.hpp"
#include "rosa/support/terminal_colors.h"
#include <array>
#include <ostream>

namespace rosa {

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

// Prints an array to the ostream.
template <typename T, size_t size>
std::ostream &operator<<(std::ostream &os, const std::array<T, size> &arr) {
  os << '[';
  for (unsigned I = 0; I < size; ++I) {
    if (I) {
      os << ',';
    }
    os << (PRINTABLE(T)) arr[I];
  }
  os << ']';
  return os;
}

} // End namespace rosa

#ifndef ROSA_ENABLE_ASSERTIONS
#define ASSERT(stmt) ROSA_IGNORE_UNUSED(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)

#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(X)
#define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X)
#endif // defined(NDEBUG)

// Static assertions are always emitted to the code.
#define STATIC_ASSERT(COND, DIAG) static_assert((COND), DIAG)

#endif // ROSA_SUPPORT_DEBUG_HPP

