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

#include <logger.h>
#include <database.h>

#include <sql/migration_0.h>

sqlite3 *global_database;
static int in_transaction;

void
database_init()
{
    int rc = sqlite3_open(global_config.database, &global_database);

    if(rc)
    {
        LOGGER_CRIT("can't open database: %s\n", sqlite3_errmsg(global_database));
        exit(1);
    }

    database_migrate();

    sqlite3_exec(global_database, "PRAGMA foreign_keys = ON", 0, 0, 0);
    in_transaction = 0;
}

void
database_free()
{
    sqlite3_close(global_database);
}

void
database_migrate()
{
    uint16_t version_num = 0;
    int s, rc;
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2(global_database, "PRAGMA user_version;", -1, &stmt, NULL);
    s = sqlite3_step(stmt);
    if (s == SQLITE_ROW)
    {
        version_num = sqlite3_column_int(stmt, 0);
    }
    else
    {
        version_num = 0;
    }

    uint16_t new_version_num = version_num;
    char* err_msg;

    sqlite3_finalize(stmt);

    switch(version_num)
    {
        case 0:
            LOGGER_INFO("migrating LEVEL 0\n");
            rc = sqlite3_exec(global_database, (const char *)sql_migration_0_sql, NULL, NULL, &err_msg);
            if(rc)
            {
                LOGGER_CRIT("couldn't migrate LEVEL 0 (%s)\n", err_msg);
                exit(1);
            }
            new_version_num = 1;
        default:
            break;
    }

    char pragma_query[32];
    sprintf(pragma_query, "PRAGMA user_version=%d;", new_version_num);
    sqlite3_exec(global_database, pragma_query, 0, 0, 0);
    LOGGER_DEBUG("storing new user_version %d\n", new_version_num);

    return;
}

int
database_transaction_begin()
{
    if(!in_transaction)
    {
        LOGGER_DEBUG("beginning transaction\n");
        sqlite3_exec(global_database, "BEGIN TRANSACTION;", NULL, NULL, NULL);
        in_transaction = 1;
        return 1;
    }
    return 0;
}

void
database_transaction_commit()
{
    LOGGER_DEBUG("commiting transaction\n");
    sqlite3_exec(global_database, "COMMIT TRANSACTION;", NULL, NULL, NULL);
    in_transaction = 0;
}

void
database_transaction_rollback()
{
    LOGGER_DEBUG("rolling back transaction\n");
    sqlite3_exec(global_database, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
    in_transaction = 0;
}

int
database_helper_get_id(sqlite3_stmt *stmt)
{
    int result = 0;

    for(;;)
    {
        int s;

        s = sqlite3_step(stmt);
        if (s == SQLITE_ROW)
        {
            result = sqlite3_column_int(stmt, 0);
        }
        else
        {
            if (s == SQLITE_DONE)
            {
                break;
            }
            else
            {
                LOGGER_ERR("error selecting id from database: %s\n", sqlite3_errstr(s));
                sqlite3_finalize(stmt);
                return 0;
            }
        }
    }

    sqlite3_finalize(stmt);

    return result;
}

int*
database_helper_get_ids(sqlite3_stmt *stmt)
{
    int *result = malloc(sizeof(int));
    int new_id;

    int row = 0;

    for(;;)
    {
        int s;

        s = sqlite3_step(stmt);
        if (s == SQLITE_ROW)
        {
            new_id = sqlite3_column_int(stmt, 0);
            if(new_id != 0) // found row for other target (relay <> schedule)
            {
                row++;

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

    sqlite3_finalize(stmt);
    result[row] = 0;

    return result;
}

char*
database_helper_get_string(sqlite3_stmt *stmt)
{
    char *result = NULL;

    for(;;)
    {
        int s;

        s = sqlite3_step(stmt);
        if (s == SQLITE_ROW)
        {
            const char *found_string = (const char *)sqlite3_column_text(stmt, 0);
            result = (char*)malloc(sizeof(char) * (strlen(found_string) + 1));
            strcpy(result, found_string);
        }
        else
        {
            if (s == SQLITE_DONE)
            {
                break;
            }
            else
            {
                LOGGER_ERR("error selecting string from database: %s\n", sqlite3_errstr(s));
                sqlite3_finalize(stmt);
                return NULL;
            }
        }
    }

    sqlite3_finalize(stmt);

    return result;
}

char**
database_helper_get_strings(sqlite3_stmt *stmt)
{
    char **result = malloc(sizeof(char*));

    int row = 0;

    for(;;)
    {
        int s;

        s = sqlite3_step(stmt);
        if (s == SQLITE_ROW)
        {
            const char *new_string = (const char *)sqlite3_column_text(stmt, 0);
            int new_string_len = strlen(new_string);
            row++;

            result = (char**)realloc(result, sizeof(char*) * (row + 1));
            result[row - 1] = malloc(sizeof(char) * (new_string_len + 1));
            strcpy(result[row - 1], new_string);
        }
        else
        {
            if(s == SQLITE_DONE)
            {
                break;
            }
            else
            {
                LOGGER_ERR("error selecting strings from database: %s\n", sqlite3_errstr(s));
                break;
            }
        }
    }
    sqlite3_finalize(stmt);
    result[row] = NULL;
    return result;
}