#include <SignalHandler.h>
#include <cassert>
#include <string>
#include <stdexcept>   // CW


//TODO: this does not allow for chaining signals. Should we do this? 
//(e.g. save old handler and call it)

SignalHandler::SignalHandler(int signum, 
                             std::function<void(siginfo_t*)> handler,
                             int flags) :
    _handler{signum,false,handler}
{
    allHandlers.emplace(signum,&_handler);
    
    struct sigaction sact;
    sact.sa_flags = SA_SIGINFO | SA_RESTART | flags;
    sact.sa_sigaction = genericHandler;
    if (sigaction(_handler.signum, &sact, nullptr) != 0) {
        throw std::runtime_error(
                std::string("Unable to register signal handler for signal")+
                std::to_string(signum));
    }
}

SignalHandler::~SignalHandler() {
    //Remove our handler
    for (auto h = allHandlers.begin(); h != allHandlers.end(); ) {
        if (h->second == &_handler) {
            h = allHandlers.erase(h);
        } else {
            ++h;
        }
    }

    //Are there other handlers?
    if (allHandlers.find(_handler.signum) == allHandlers.end()) {
        //No? Then remove handler and set to default action ...
        struct sigaction sact;
        sact.sa_flags = 0;
        sact.sa_handler = SIG_DFL;
        sigaction(_handler.signum, &sact, nullptr);
        //Can not fail if we created signal correctly. 
    }
}

void SignalHandler::enableHandler(bool value) {
    _handler.ignore = value;
}

void SignalHandler::genericHandler(int signum, siginfo_t *si, void*) {
    assert(signum == si->si_signo);
    auto it = allHandlers.equal_range(signum);
    for (auto h = it.first; h != it.second; ++h) {
        Handler* hdlr = h->second;
        if (!hdlr->ignore) hdlr->handler(si);
    }
}

void SignalHandler::ignoreSignal(int signum) {
    struct sigaction sact;

    sact.sa_flags = 0;

    /* Ignore SIGPIPE. Accessing dead pipes now returns EPIPE. */
    sact.sa_handler = SIG_IGN;
    if (sigaction(signum, &sact, nullptr) != 0) {
        throw std::runtime_error(
                std::string("Unable to ignore signal")+std::to_string(signum));
    }
}
