#include "main.hpp"


CONF_DEF(config) {
    ConfigKey port;
};
CONF_INIT(config) {
    CONF_KEY_INIT(port, false, 4444);
}
static Config* global_config = NULL;


const int* main_fd = NULL;


int main(int argc, char **argv) {
    EINTR_RETRY(close(STDIN_FILENO));
    EINTR_RETRY(close(STDOUT_FILENO));

    if (argc != 2) {
        STDERR("Usage: %s <config-file>", argv[0]);
        return 1;
    }
    Hooks::run(Hooks::INIT);
    global_config = new Config(argv[1]);
    if (!global_config->load()) {
        log(error, "cannot load config '%s'", argv[1]);
        delete global_config;
        Hooks::run(Hooks::DEINIT);
        return 1;
    }
    log(notice, "loaded config '%s'", argv[1]);

    Hooks::run(Hooks::PRE_IO);

    // open main socket
    int fd = config? socket_listen(config->port.val.num): -1;
    main_fd = &fd;
    if (fd == -1) {
        delete global_config;
        Hooks::run(Hooks::DEINIT);
        return 1;
    }

    // init
    Poll* poll = Poll::getInst();
    poll->add(fd, &CtxAcceptor::accept_handler, EVENT_IN|EVENT_CLOSE);

    // main loop
    time_t shutdown_at = 0;
    shutdown_e shutdown_last = SHUTDOWN_NONE;
    while (true) {
        if (unlikely(SHUTDOWN)) {
            if (fd != -1) { // closing main fd in any case (once)
                log(info, "won't accept new connections");
                poll->del(fd);
                EINTR_RETRY(close(fd));
                fd = -1;
            }
            if (!poll->refcount()) { // already everything closed?
                break;
            }
            if (!shutdown_at) { // first encounter
                shutdown_at = NOW;
            }
            if (SHUTDOWN == SHUTDOWN_GRACEFUL && shutdown_at < NOW-3) { // escalate after 3 secs
                SHUTDOWN = SHUTDOWN_NOW;
            }
            if (SHUTDOWN == SHUTDOWN_NOW && shutdown_at < NOW-10) { // waited long enough
                break;
            }
            if (SHUTDOWN != shutdown_last) { // broadcast if shutdown condition changed
                shutdown_last = SHUTDOWN;
                poll->wakeup();
            }
        } else if (unlikely(RECONFIGURE)) {
            log(notice, "triggering config reload...");
            reconfigure(global_config);
            RECONFIGURE = 0;
            CLEANUP = 0; // implicitly done upon config change
        } else if (unlikely(CLEANUP)) {
            log(notice, "triggering cache clean...");
            Hooks::run(Hooks::CACHE_CLEAN);
            CLEANUP = 0;
        }

        poll->wait(1000);
    }

    // done.
    log(notice, "shutting down");
    Hooks::run(Hooks::POST_IO); // joins pending config aios, pollers, etc
    delete global_config;
    Hooks::run(Hooks::DEINIT);
}