#include "pch.h"
#include "CSQLite/CSQLite.h"
#include "aLog.h"

int CSQLiteDB::open(const char* apFilename, const int   aFlags) noexcept
{
    mFilename = apFilename;
    dbErrCode = sqlite3_open_v2(apFilename, &mpSQLite, aFlags, nullptr);
    if (SQLITE_OK != dbErrCode)
    {
        dbErrMsg = sqlite3_errmsg(mpSQLite);
        sqlite3_close(mpSQLite); // close is required even in case of error on opening
        mpSQLite = nullptr;
    }
    return dbErrCode;
}

int CSQLiteDB::open(const std::string &apFilename, const int   aFlags) noexcept
{
    mFilename = apFilename;
    dbErrCode = sqlite3_open_v2(apFilename.c_str(), &mpSQLite, aFlags, nullptr);
    if (SQLITE_OK != dbErrCode)
    {
        dbErrMsg = sqlite3_errmsg(mpSQLite);
        sqlite3_close(mpSQLite); // close is required even in case of error on opening
        mpSQLite = nullptr;
    }
    return dbErrCode;
}

int CSQLiteDB::openInRam(const char* apFilename)
{
    UNUSED(apFilename);
    return dbErrCode;

}

int CSQLiteDB::close()
{
	if(bInTransaction)
	{
		// 没有主动Commit，就Rollback();
        Rollback();
	}
    bInTransaction = false;
    int ret =0;
    if(mpSQLite){
        ret = sqlite3_close(mpSQLite);

        if(SQLITE_OK != ret){
            switch (ret) {
            case SQLITE_MISUSE:
                alog->error("CSQLiteDB::close(): Library used incorrectly {}, {}", ret, mFilename);
                break;
            case SQLITE_BUSY:
                alog->error("CSQLiteDB::close(): database is locked {}, {}", ret, mFilename);
                break;
            default:
				alog->error("CSQLiteDB::close(): database close error {}, {}", ret, mFilename);
                break;
            }
        }
        mpSQLite = nullptr;
    }
    return ret;
}


/**
 * @brief Set a busy handler that sleeps for a specified amount of time when a table is locked.
 *
 *  This is useful in multithreaded program to handle case where a table is locked for writting by a thread.
 *  Any other thread cannot access the table and will receive a SQLITE_BUSY error:
 *  setting a timeout will wait and retry up to the time specified before returning this SQLITE_BUSY error.
 *  Reading the value of timeout for current connection can be done with SQL query "PRAGMA busy_timeout;".
 *  Default busy timeout is 0ms.
 *
 * @param[in] aBusyTimeoutMs    Amount of milliseconds to wait before returning SQLITE_BUSY
 *
 * @throw SQLite::Exception in case of error
 */
void CSQLiteDB::setBusyTimeout(const int aBusyTimeoutMs)
{
    const int ret = sqlite3_busy_timeout(mpSQLite, aBusyTimeoutMs);
    check(ret);
}

// Shortcut to execute one or multiple SQL statements without results (UPDATE, INSERT, ALTER, COMMIT, CREATE...).
int CSQLiteDB::exec(const char* apQueries)
{
    const int ret = sqlite3_exec(mpSQLite, apQueries, nullptr, nullptr, nullptr);
    check(ret);

    // Return the number of rows modified by those SQL statements (INSERT, UPDATE or DELETE only)
    return sqlite3_changes(mpSQLite);
}

// Shortcut to test if a table exists.
bool CSQLiteDB::tableExists(const char* apTableName)
{
    CSQLiteStatement query(*this, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name=?");
    query.bind(1, apTableName);
    (void)query.executeStep(); // Cannot return false, as the above query always return a result
    return (1 == query.getColumn(0).getInt());
}
