#ifndef SUPPORT_ASSERT_CHECKPOINT_H
#define SUPPORT_ASSERT_CHECKPOINT_H

#include <csignal>
#include <cstdlib>
#include <iostream>

struct Checkpoint
{
  const char* file;
  const char* func;
  int line;
  const char* msg;

  Checkpoint()
      : file(nullptr)
      , func(nullptr)
      , line(-1)
      , msg(nullptr)
  {}
  Checkpoint(const char* xfile, const char* xfunc, int xline, const char* xmsg)
      : file(xfile)
      , func(xfunc)
      , line(xline)
      , msg(xmsg)
  {}

  template <class Stream>
  void print(Stream& s) const
  {
    if (!file)
    {
      s << "NO CHECKPOINT\n";
      return;
    }
    s << file << ":" << line << " " << func << ": Checkpoint";
    if (msg)
    {
      s << " '" << msg << "'";
    }
    s << std::endl;
  }
};

inline Checkpoint& globalCheckpoint()
{
  static Checkpoint C;
  return C;
}

inline void clearCheckpoint()
{
  globalCheckpoint() = Checkpoint();
}

#if defined(__GNUC__)
#  define CHECKPOINT_FUNCTION_NAME __PRETTY_FUNCTION__
#else
#  define CHECKPOINT_FUNCTION_NAME __func__
#endif

#define CHECKPOINT(msg) globalCheckpoint() = Checkpoint(__FILE__, CHECKPOINT_FUNCTION_NAME, __LINE__, msg);

inline void checkpointSignalHandler(int signal)
{
  if (signal == SIGABRT)
  {
    globalCheckpoint().print(std::cerr);
  }
  else
  {
    std::cerr << "Unexpected signal " << signal << " received\n";
  }
  std::_Exit(EXIT_FAILURE);
}

inline bool initCheckpointHandler()
{
  typedef void (*HandlerT)(int);
  static bool isInit = false;
  if (isInit)
  {
    return true;
  }
  HandlerT prev_h = std::signal(SIGABRT, checkpointSignalHandler);
  if (prev_h == SIG_ERR)
  {
    std::cerr << "Setup failed.\n";
    std::_Exit(EXIT_FAILURE);
  }
  isInit = true;
  return false;
}

static bool initDummy = initCheckpointHandler();

#endif
