#include "SQLiteConnection.h"



SQLitePreparedStatement::SQLitePreparedStatement(SQLitePreparedStatement &&rhs)
    : pStatement(rhs.pStatement)
{
    rhs.pStatement = nullptr;
}

SQLitePreparedStatement &SQLitePreparedStatement::operator=(SQLitePreparedStatement &&rhs)
{
    Free();
    pStatement = rhs.pStatement;
    rhs.pStatement = nullptr;
    return *this;
}

SQLitePreparedStatement::~SQLitePreparedStatement()
{
    Free();
}

void SQLitePreparedStatement::Open(SQLiteConnection &db, const char *query)
{
    int result = sqlite3_prepare_v2(
        db.pDb,
        query,
        -1,
        &pStatement,
        nullptr);
    db.CheckResult(result);
}

void SQLitePreparedStatement::Open(SQLiteConnection &db, const QString &query)
{
    Open(db, query.toUtf8().constData());
}

void SQLitePreparedStatement::Bind(int index, const char *s, int length)
{
    int result = sqlite3_bind_text(pStatement, index, s, length, SQLITE_STATIC);
    if (result != SQLITE_OK)
        throw SQLiteException("failed to bind");
}

void SQLitePreparedStatement::Bind(int index, const QString &s)
{
    int result = sqlite3_bind_text16(pStatement, index, s.constData(), -1, SQLITE_TRANSIENT);
    if (result != SQLITE_OK)
        throw SQLiteException("failed to bind");
}

void SQLitePreparedStatement::Bind(int index, int v)
{
    int result = sqlite3_bind_int(pStatement, index, v);
    if (result != SQLITE_OK)
        throw SQLiteException("failed to bind");
}

const char *SQLitePreparedStatement::ColumnCharPtr(int col)
{
    return (const char*)sqlite3_column_text(pStatement, col);
}

QString SQLitePreparedStatement::ColumnText(int col)
{
    const unsigned char *val = sqlite3_column_text(pStatement, col);
    if (val != nullptr)
    {
        return QString::fromUtf8(val);
    }
    return {};
}

int SQLitePreparedStatement::ColumnInteger(int col)
{
    return sqlite3_column_int(pStatement, col);
}

bool SQLitePreparedStatement::Step()
{
    int result = sqlite3_step(pStatement);
    switch (result)
    {
    case SQLITE_ROW:
        return true;
    case SQLITE_DONE:
        return false;
    default:
        throw std::runtime_error("Error in step");
    }
}

SQLiteConnection::SQLiteConnection(SQLiteConnection &&rhs)
    : pDb(rhs.pDb)
{
    rhs.pDb = nullptr;
}

SQLiteConnection &SQLiteConnection::operator=(SQLiteConnection &&rhs)
{
    if (pDb != nullptr)
    {
        sqlite3_close(pDb);
    }
    pDb = rhs.pDb;
    rhs.pDb = nullptr;
    return *this;
}

SQLiteTransaction::SQLiteTransaction(SQLiteConnection &db)
    : db(db)
{
    SQLitePreparedStatement stmt = db.Prepare("BEGIN TRANSACTION;");
    stmt.Step();
    inTransaction = true;
}

SQLiteTransaction::~SQLiteTransaction()
{
    if (inTransaction)
    {
        UncheckedRollback();
    }
}

void SQLiteTransaction::Commit()
{
    if (inTransaction)
    {
        SQLitePreparedStatement stmt = db.Prepare("COMMIT TRANSACTION;");
        stmt.Step();
        inTransaction = false;
    }
    else
    {
        throw SQLiteException("Transaction already ended or never started");
    }
}

void SQLiteTransaction::Rollback()
{
    if (inTransaction)
    {
        UncheckedRollback();
    }
    else
    {
        throw SQLiteException("Transaction already ended or never started");
    }
}

void SQLiteTransaction::UncheckedRollback()
{
    SQLitePreparedStatement stmt = db.Prepare("ROLLBACK TRANSACTION;");
    stmt.Step();
    inTransaction = false;
}
