#include <stdlib.h>
#include <stdint.h>
#include <stddef.h>

#include <models/macro_action.h>
#include <logger.h>
#include <cache.h>
#include <database.h>

static macro_action_t*
macro_action_db_select_mapper(sqlite3_stmt *stmt)
{
    macro_action_t *new_macro_action = malloc(sizeof(macro_action_t));
    for(int i = 0; i < sqlite3_column_count(stmt); i++)
    {
        const char *name = sqlite3_column_name(stmt, i);
        switch(name[0])
        {
            case 'm': // macro_id
                new_macro_action->macro_id = sqlite3_column_int(stmt, i);
                break;
            case 'r': // relay_id
                new_macro_action->relay_id = sqlite3_column_int(stmt, i);
                break;
            case 's': // schedule_id
                new_macro_action->schedule_id = sqlite3_column_int(stmt, i);
                break;
            case 'w': // weekday
                new_macro_action->weekday = (uint8_t)sqlite3_column_int(stmt, i);
                break;
            default: // ignore columns not implemented
                break;
        }
    }
    return new_macro_action;
}

static macro_action_t**
macro_action_db_select(sqlite3_stmt *stmt)
{
    macro_action_t **all_macro_actions = malloc(sizeof(macro_action_t*));

    int row = 0;

    for(;;)
    {
        int s;

        s = sqlite3_step(stmt);
        if (s == SQLITE_ROW)
        {
            macro_action_t *new_macro_action = macro_action_db_select_mapper(stmt);
            row++;

            all_macro_actions = (macro_action_t**)realloc(all_macro_actions, sizeof(macro_action_t*) * (row + 1));
            all_macro_actions[row - 1] = new_macro_action;
        }
        else
        {
            if(s == SQLITE_DONE)
            {
                break;
            }
            else
            {
                LOGGER_ERR("error selecting macro_actions from database: %s\n", sqlite3_errstr(s));
                break;
            }
        }
    }
    sqlite3_finalize(stmt);
    all_macro_actions[row] = NULL;
    return all_macro_actions;
}

int
macro_action_insert(macro_action_t *macro_action)
{
    sqlite3_stmt *stmt;

    sqlite3_prepare_v2(global_database, "INSERT INTO macro_actions(macro_id, relay_id, schedule_id, weekday) values (?1, ?2, ?3, ?4);", -1, &stmt, NULL);

    sqlite3_bind_int(stmt, 1, macro_action->macro_id);
    sqlite3_bind_int(stmt, 2, macro_action->relay_id);
    sqlite3_bind_int(stmt, 3, macro_action->schedule_id);
    sqlite3_bind_int(stmt, 4, macro_action->weekday);

    int rc = sqlite3_step(stmt);

    sqlite3_finalize(stmt);

    cache_invalidate_macro(macro_action->macro_id);

    return rc != SQLITE_DONE;
}

int
macro_action_delete_for_macro(int macro_id)
{
    sqlite3_stmt *stmt;

    sqlite3_prepare_v2(global_database, "DELETE FROM macro_actions WHERE macro_id=?1;", -1, &stmt, NULL);
    sqlite3_bind_int(stmt, 1, macro_id);

    int rc = sqlite3_step(stmt);

    sqlite3_finalize(stmt);

    cache_invalidate_macro(macro_id);

    return rc != SQLITE_DONE;
}

macro_action_t**
macro_action_get_for_macro(int macro_id)
{
    LOGGER_DEBUG("getting macro_actions [macro_id=%d] from database\n", macro_id);
    sqlite3_stmt *stmt;

    sqlite3_prepare_v2(global_database, "SELECT * FROM macro_actions WHERE macro_id=?", -1, &stmt, NULL);
    sqlite3_bind_int(stmt, 1, macro_id);

    return macro_action_db_select(stmt);
}

int
macro_action_execute(macro_action_t *macro_action)
{
    schedule_t *schedule = schedule_get_by_id(macro_action->schedule_id);
    if(!schedule)
    {
        return 1;
    }

    relay_t *relay = relay_get_by_id(macro_action->relay_id);
    if(!relay)
    {
        free(schedule);
        return 1;
    }
    
    schedule_free(relay->schedules[macro_action->weekday]);
    relay->schedules[macro_action->weekday] = schedule;

    return relay_save(relay);
}

void
macro_action_free_list(macro_action_t **macro_actions)
{
    for(int i = 0; macro_actions[i] != NULL; ++i)
    {
        free(macro_actions[i]);
    }
    free(macro_actions);
}

int*
macro_action_get_macro_ids_with_schedule(int schedule_id)
{
    sqlite3_stmt *stmt;

    sqlite3_prepare_v2(global_database, "SELECT macro_id FROM macro_actions WHERE schedule_id=?1;", -1, &stmt, NULL);
    sqlite3_bind_int(stmt, 1, schedule_id);

    return database_helper_get_ids(stmt);
}

int*
macro_action_get_macro_ids_with_relay(int relay_id)
{
    sqlite3_stmt *stmt;

    sqlite3_prepare_v2(global_database, "SELECT macro_id FROM macro_actions WHERE relay_id=?1;", -1, &stmt, NULL);
    sqlite3_bind_int(stmt, 1, relay_id);

    return database_helper_get_ids(stmt);
}