#ifndef DEBUG_H
#define DEBUG_H

// See the Makevars file to see how to compile with various debugging settings.

#if defined(DEBUG_THREAD)
#include "tinycthread.h"

extern tct_thrd_t __main_thread__;
extern tct_thrd_t __background_thread__;

// This must be called from the main thread so that thread assertions can be
// tested later.
#define REGISTER_MAIN_THREAD()       __main_thread__ = tct_thrd_current();
#define REGISTER_BACKGROUND_THREAD() __background_thread__ = tct_thrd_current();
#define ASSERT_MAIN_THREAD()         assert(tct_thrd_current() == __main_thread__);
#define ASSERT_BACKGROUND_THREAD()   assert(tct_thrd_current() == __background_thread__);

#else
#define REGISTER_MAIN_THREAD()
#define REGISTER_BACKGROUND_THREAD()
#define ASSERT_MAIN_THREAD()
#define ASSERT_BACKGROUND_THREAD()

#endif // defined(DEBUG_THREAD)


// ============================================================================
// Logging
// ============================================================================

void err_printf(const char *fmt, ...);

enum LogLevel {
  LOG_OFF,
  LOG_ERROR,
  LOG_WARN,
  LOG_INFO,
  LOG_DEBUG
};

extern LogLevel log_level_;

// This is a macro instead of a function, so that if msg is an expression that
// involves constructing a string, the string construction does not need to be
// executed when the message is not being logged. If it were a function, the
// expression would need to be executed even when the message is not actually
// logged.
//
// Conversion to std::string is done so that msg can be a char* or a
// std::string. This method is needed because macros can't be overloaded.
#define DEBUG_LOG(msg, level) if (log_level_ >= level) err_printf("%s\n", std::string(msg).c_str());

#endif
