#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#include <lmdb.h>
#include <signal.h>
#include <syslog.h>

#include <cli.h>
#include <logger.h>
#include <mongoose.h>
#include <models/controller.h>
#include <database.h>
#include <config.h>
#include <connections.h>
#include <constants.h>
#include <handlers.h>
#include <drivers.h>
#include <enums.h>
#include <runners.h>
#include <helpers.h>
#include <wiringPi.h>
#include <piFace.h>
#include <wiring_debug.h>

static struct mg_mgr mgr;

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

    // TODO fix mg_mgr_free() causing loop (can't terminate)
    //LOGGER_DEBUG("freeing mongoose manager\n");
    //mg_mgr_free(&mgr);

    LOGGER_DEBUG("closing database\n");
    database_free();

    LOGGER_DEBUG("freeing global controller\n");
    controller_free(global_controller);

    LOGGER_DEBUG("freeing relay configs config\n");
    free(global_config->relay_configs);

    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)
{
    (void)argc;
    (void)argv;

    signal(SIGINT, terminate);
    signal(SIGABRT, terminate);
    signal(SIGTERM, terminate);

    openlog("emgauwa-controller", 0, LOG_USER);
    setlogmask(LOG_UPTO(LOG_INFO));


    /******************** 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);
    }

    if(sizeof(time_t) < 8)
    {
        LOGGER_WARNING("this system is not using 8-bit time\n");
    }


    /******************** INIT DATABASE, SOCKETS AND THIS CONTROLLER ********************/

    mg_mgr_init(&mgr, NULL);

    database_init();

    global_controller = controller_load();
    if(!global_controller)
    {
        global_controller = controller_create();
        controller_save();
    }

    connection_discovery_bind(&mgr);
    connection_mqtt_connect(&mgr);
    struct mg_connection *c_command = connection_command_bind(&mgr);

    if(global_controller->command_port == 0)
    {
        global_controller->command_port = helper_get_port(c_command->sock);
        controller_save();
    }

    controller_debug(global_controller);

    helper_drop_privileges();

    
    /******************** SETUP WIRINGPI ********************/

    wiringPiSetup();

    int piface_setup = 0;

    for(uint_fast8_t i = 0; i < global_config->relay_count; ++i)
    {
        int relay_default = global_config->relay_configs[i].init;
        if(relay_default == -1)
        {
            relay_default = global_config->relays_init;
        }
        if(relay_default == -1)
        {
            relay_default = global_config->relay_configs[i].inverted;
        }

        if(global_config->relay_configs[i].driver == RELAY_DRIVER_GPIO)
        {
            pinMode(global_config->relay_configs[i].pin, OUTPUT);
            driver_gpio_set(global_config->relay_configs[i].pin, relay_default);
        }
        if(global_config->relay_configs[i].driver == RELAY_DRIVER_PIFACE)
        {
            if(!piface_setup)
            {
                piFaceSetup(PIFACE_GPIO_BASE);
                piface_setup = 1;
            }
            driver_piface_set(global_config->relay_configs[i].pin, relay_default);
        }
    }


    /******************** CHECK FOR TESTING RUN ********************/

    if(cli.demo_mode)
    {
        runner_test(global_controller);
        terminate(0);
    }
    

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

    time_t timer = 0;

    for (;;)
    {
        mg_mgr_poll(&mgr, 200);
        if(time(NULL) - timer >= 1)
        {
            if(!global_connection_mqtt)
            {
                connection_mqtt_connect(&mgr);
            }
            handler_loop(global_connection_mqtt);
            timer = time(NULL);
        }
    }

    terminate(0);

    return 0;
}