#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>

#include <mongoose.h>

#include <cache.h>
#include <cli.h>
#include <config.h>
#include <database.h>
#include <enums.h>
#include <handlers.h>
#include <helpers.h>
#include <logger.h>
#include <models/controller.h>
#include <router.h>
#include <status.h>

static struct mg_mgr mgr;

static void
terminate(int signum)
{
    LOGGER_INFO("terminating controller (%d)\n", signum);

    mg_mgr_free(&mgr);

    database_free();
    router_free();
    status_free();
    cache_free();
    config_free();

    closelog();

    exit(signum);
}

/**
 * @brief The main function
 *
 * @param argc UNUSED
 * @param argv UNUSED
 *
 * @return Statuscode to indicate success (0) or failure (!0)
 */
int
main(int argc, const char** argv)
{
    signal(SIGINT, terminate);
    signal(SIGABRT, terminate);
    signal(SIGTERM, terminate);

    openlog("emgauwa-core", 0, LOG_USER);
    setlogmask(LOG_UPTO(LOG_DEBUG));

    /******************** LOAD CONFIG ********************/

    config_init();

    cli_t cli;
    cli_parse(argc, argv, &cli);

    config_load(global_config, cli.config_file);

    if(global_config->logging.file == NULL)
    {
        global_config->logging.file = stdout;
    }

    if(global_config->include)
    {
        config_load_directory(global_config, global_config->include);
    }


    /******************** SETUP CONNECTION ********************/

    struct mg_mqtt_broker brk;

    mg_mgr_init(&mgr, NULL);

    char address[100];
    sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.server);
    struct mg_connection *c_http = mg_bind(&mgr, address, handler_http);
    if(c_http == NULL)
    {
        LOGGER_CRIT("failed to bind http server to port %u\n", global_config->ports.server);
        exit(1);
    }
    mg_set_protocol_http_websocket(c_http);

    sprintf(address, "tcp://0.0.0.0:%u", global_config->ports.mqtt);
    struct mg_connection *c_mqtt = mg_bind(&mgr, address, handler_mqtt);
    if(c_mqtt == NULL)
    {
        LOGGER_CRIT("failed to bind mqtt server to port %u\n", global_config->ports.mqtt);
        exit(1);
    }
    mg_mqtt_broker_init(&brk, NULL);
    c_mqtt->priv_2 = &brk;
    mg_set_protocol_mqtt(c_mqtt);

    helper_drop_privileges();

    memset(&global_config->http_server_opts, 0, sizeof(global_config->http_server_opts));
    global_config->http_server_opts.document_root = global_config->content_dir;
    global_config->http_server_opts.enable_directory_listing = "no";
    global_config->http_server_opts.extra_headers = "Access-Control-Allow-Origin: *\r\nAccess-Control-Allow-Headers: *\r\nAccess-Control-Allow-Methods: *";


    /******************** INIT COMPONENTS ********************/

    database_init();
    cache_init();
    router_init();
    status_init();


    /******************** START MAIN LOOP ********************/

    for (;;)
    {
        mg_mgr_poll(&mgr, 200);
        status_broadcast(&mgr);
    }

    terminate(0);
}