#include "handle.h"

#include <assert.h>
#include <signal.h>
#include <stdbool.h>
#include <stdio.h>

static void (*handler) (int);
static volatile int caught_signal;
static volatile bool handler_set;

#define SIGNALS \
SIGNAL(SIGABRT) \
SIGNAL(SIGBUS) \
SIGNAL(SIGINT) \
SIGNAL(SIGSEGV) \
SIGNAL(SIGTERM)

// *INDENT-OFF*

#define SIGNAL(SIG) \
static void (*SIG ## _handler)(int);
SIGNALS
#undef SIGNAL

const char *
kissat_signal_name (int sig)
{
#define SIGNAL(SIG) \
  if (sig == SIG) return #SIG;
  SIGNALS
#undef SIGNAL
  if (sig == SIGALRM)
    return "SIGALRM";
  return "SIGUNKNOWN";
}

void
kissat_reset_signal_handler (void)
{
  if (!handler_set)
    return;
#define SIGNAL(SIG) \
  signal (SIG, SIG ## _handler);
  SIGNALS
#undef SIGNAL
  handler_set = false;
  handler = 0;
}

// *INDENT-ON*

static void
catch_signal (int sig)
{
  if (caught_signal)
    return;
  caught_signal = sig;
  assert (handler_set);
  assert (handler);
  handler (sig);
  kissat_reset_signal_handler ();
  raise (sig);
}

void
kissat_init_signal_handler (void (*h) (int sig))
{
  assert (!handler);
  handler = h;
  handler_set = true;
#define SIGNAL(SIG) \
  SIG ##_handler = signal (SIG, catch_signal);
  SIGNALS
#undef SIGNAL
}

static volatile bool caught_alarm;
static volatile bool alarm_handler_set;
static void (*SIGALRM_handler) (int);
static void (*handle_alarm) ();

static void
catch_alarm (int sig)
{
  assert (sig == SIGALRM);
  if (caught_alarm)
    return;
  if (!alarm_handler_set)
    raise (sig);
  assert (handle_alarm);
  caught_alarm = true;
  handle_alarm ();
}

void
kissat_init_alarm (void (*handler) (void))
{
  assert (handler);
  assert (!caught_alarm);
  handle_alarm = handler;
  alarm_handler_set = true;
  assert (!SIGALRM_handler);
  SIGALRM_handler = signal (SIGALRM, catch_alarm);
}

void
kissat_reset_alarm (void)
{
  assert (alarm_handler_set);
  assert (handle_alarm);
  alarm_handler_set = false;
  handle_alarm = 0;
  (void) signal (SIGALRM, SIGALRM_handler);
}