#include "common.hpp"
#include "config.hpp"
#include "sock.hpp"
#include "poll.hpp"
#include "server.hpp"
#include "nickserv.hpp"
#include "ssl.hpp"
#include <unistd.h>
#include <assert.h>


int main(int argc, char** argv) {
    // basic setup
    update_now();
    close(0);
    close(1);

    // parse config
    bool systemd = false;
    const char* conffile = NULL;
    if (argc == 2) {
        conffile = argv[1];
    } else if (argc == 3) {
        if (strcmp(argv[1], "--systemd") != 0) {
            LOG("Unknown argument '%s'", argv[1]);
            return 1;
        }
        systemd = true;
        conffile = argv[2];
    } else {
        LOG("Usage: %s [--systemd] <config-file>", argv[0]);
        return 1;
    }
    if (!parse_config(conffile)) {
        log_usage();
        return 1;
    }

    // handlers
    register_signals();

    // default-handlers - ssl?
    io_handlers.pre_accept_handler = NULL;
    io_handlers.accept_handler = &Client::createInst;
    io_handlers.read_handler = &read;
    io_handlers.write_handler = &write;
    io_handlers.close_handler = &close;
#ifdef USE_OPENSSL
    if (atoi(config.ssl.str)) {
        if (!ssl_init(config.ssl_cert.str, config.ssl_key.str)) {
            LOG("cannot initialize ssl");
            ssl_deinit();
            return 1;
        }
        io_handlers.pre_accept_handler = &SslClient::createInst;
        io_handlers.read_handler = &SslClient::ssl_read;
        io_handlers.write_handler = &SslClient::ssl_write;
        io_handlers.close_handler = &SslClient::shutdown;
    }
#endif

    // listen on port(s)
    std::vector<int> fds;
    if (systemd) {
        if (!socket_listen(fds)) return 1;
        assert(fds.size());
        LOG("inherited %zu listening ports, ignoring %zu configured ones...", fds.size(), config.port.strs.size());
    } else {
        assert(config.port.strs.size()); // mandatory config option(s)
        for (std::vector<const char*>::const_iterator it = config.port.strs.begin(); it != config.port.strs.end(); ++it) {
            int port = atoi(*it);
            int fd = (port > 0)? socket_listen(port): -1;
            if (fd == -1) return 1;
            fds.push_back(fd);
            LOG("listening on :%d...", port);
        }
    }

    // drop privs & goto jail
    if (!goto_jail(config.chroot_user.str, config.chroot_dir.str)) {
        return 1;
    }

    // init
    Server* server = new Server();
    Poll* poll = Poll::getInst();
    for (std::vector<int>::const_iterator it = fds.begin(); it != fds.end(); ++it) {
        poll->add(*it, &socket_accept, EVENT_IN);
    }
    (void)NickServ::getInst(); // early init

    // main loop
    while (!SHUTDOWN) {
        if (!poll->wait(1000)) break;
        if (systemd && poll->num_fds() <= fds.size() && poll->last_event() < NOW - SOCK_TIMEOUT) {
            LOG("idle timeout reached");
            break;
        }
        usleep(100000); // event collect interval
    }
    LOG("shutting down");
    // TODO: if (systemd) sd_notify(false, "STOPPING=1\n" "STATUS=Shutting down.");

    // don't accept anymore & kick all clients & cleanup
    while (fds.size()) {
        poll->del(fds.back());
        if (!systemd) { // keep inherited systemd fds open
            close(fds.back());
        }
        fds.pop_back();
    }
    delete server;
    delete poll;
    delete NickServ::getInst();
#ifdef USE_OPENSSL
    if (atoi(config.ssl.str)) {
        ssl_deinit();
    }
#endif
    free_config();

    return 0;
}