#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);
    }

    if(database_migrate())
    {
        exit(1);
    }

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

void
database_free()
{
    sqlite3_close(global_database);
}

int
database_migrate()
{
    uint16_t version_num = 0;
    int s, rc;
    sqlite3_stmt *stmt;
    sqlite3_prepare_v2(global_database, "SELECT version_num FROM meta LIMIT 1;", -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);
                break;
            }
            new_version_num = 1;
        default:
            break;
    }

    if(version_num == 0)
    {
        sqlite3_prepare_v2(global_database, "INSERT INTO meta (version_num) VALUES (?1);", -1, &stmt, NULL);
    }
    else
    {
        sqlite3_prepare_v2(global_database, "UPDATE meta SET version_num=?1;", -1, &stmt, NULL);
    }
    sqlite3_bind_int(stmt, 1, new_version_num);

    rc = sqlite3_step(stmt);
    if (rc != SQLITE_DONE)
    {
        LOGGER_CRIT("couldn't write new schema version\n");
    }

    sqlite3_finalize(stmt);

    return rc != SQLITE_DONE;
}

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;
}