#include "CdbConnectionPool.h"

#include "utils/SettingConfig.h"

/*-----------------------------------------------------------------------------|
 |                         CDbConnectionPoolPrivate 的定义                      |
 |----------------------------------------------------------------------------*/
class CDbConnectionPoolPrivate
{
public:
    CDbConnectionPoolPrivate();

    // 数据库信息
    QString databaseTypeLocal;
    QString hostNameLocal;
    QString databaseNameLocal;
    QString usernameLocal;
    QString passwordLocal;
    int portLocal;

    QString databaseTypeRemote;
    QString hostNameRemote;
    QString databaseNameRemote;
    QString usernameRemote;
    QString passwordRemote;

    bool    testOnBorrow;    // 取得连接的时候验证连接有效
    QString testOnBorrowSql; // 测试访问数据库的 SQL
};

CDbConnectionPoolPrivate::CDbConnectionPoolPrivate()
{
    // 可以从配置文件里读取 ---- 这里直接给出，记得修改！！！
    testOnBorrow = true;
    testOnBorrowSql = "SELECT 1";

    // MYSQL 直连
    //databaseTypeLocal = "QMYSQL";
    databaseTypeLocal = SettingConfig::getInstance().LOCAL_Mode;
    hostNameLocal = SettingConfig::getInstance().LOCAL_HOST;
    databaseNameLocal = SettingConfig::getInstance().LOCAL_ODBC_NAME;
    usernameLocal = SettingConfig::getInstance().LOCAL_USERNAME;
    passwordLocal = SettingConfig::getInstance().LOCAL_PASSWORD;
    portLocal = SettingConfig::getInstance().LOCAL_PORT.toInt();

    databaseTypeRemote = "QODBC";
    hostNameRemote = SettingConfig::getInstance().REMOTE_HOST;
    databaseNameRemote = SettingConfig::getInstance().REMOTE_ODBC_NAME;
    usernameRemote = SettingConfig::getInstance().REMOTE_USERNAME;
    passwordRemote = SettingConfig::getInstance().REMOTE_PASSWORD;
}

/*-----------------------------------------------------------------------------|
 |                            CDbConnectionPool 的定义                          |
 |----------------------------------------------------------------------------*/
CDbConnectionPool::CDbConnectionPool() : d(new CDbConnectionPoolPrivate)
{
}

CDbConnectionPool::~CDbConnectionPool()
{
    delete d;
}

QSqlDatabase CDbConnectionPool::openConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase(d->databaseTypeLocal);
    db.setHostName(d->hostNameLocal);
    db.setDatabaseName(d->databaseNameLocal);
    db.setUserName(d->usernameLocal);
    db.setPassword(d->passwordLocal);
    db.setPort(d->portLocal);

    if (db.open())
    {
        qDebug().noquote() << QString("QMYSQL Connection created");
        return db;
    }
    else
    {
        qDebug().noquote() << "Create connection error:" << db.lastError().text();
        return QSqlDatabase();
    }
}

// 获取数据库连接
QSqlDatabase CDbConnectionPool::openConnection(const QString type, const QString &connectionName)
{
    // 1. 创建连接的全名: 基于线程的地址和传入进来的 connectionName，因为同一个线程可能申请创建多个数据库连接
    // 2. 如果连接已经存在，复用它，而不是重新创建
    //    2.1 返回连接前访问数据库，如果连接断开，可以重新建立连接 (测试: 关闭数据库几分钟后再启动，再次访问数据库)
    // 3. 如果连接不存在，则创建连接
    // 4. 线程结束时，释放在此线程中创建的数据库连接

    // [1] 创建连接的全名: 基于线程的地址和传入进来的 connectionName，因为同一个线程可能申请创建多个数据库连接
    QString baseConnectionName = type + "_conn_" + QString::number(quint64(QThread::currentThread()), 16);
    QString fullConnectionName = baseConnectionName + connectionName;

    if (QSqlDatabase::contains(fullConnectionName))
    {
        // [2] 如果连接已经存在，复用它，而不是重新创建
        QSqlDatabase existingDb = QSqlDatabase::database(fullConnectionName);

        if (d->testOnBorrow)
        {
            // [2.1] 返回连接前访问数据库，如果连接断开，可以重新建立连接 (测试: 关闭数据库几分钟后再启动，再次访问数据库)
//            qDebug().noquote() << QString("Test connection on borrow, execute: %1, for connection %2").arg(d->testOnBorrowSql).arg(fullConnectionName);
            QSqlQuery query(d->testOnBorrowSql, existingDb);

            if (query.lastError().type() != QSqlError::NoError && !existingDb.open())
            {
                qDebug().noquote() << "Open datatabase error:" << existingDb.lastError().text();
                return QSqlDatabase();
            }
        }

        return existingDb;
    }
    else
    {
        // [3] 如果连接不存在，则创建连接
        if (qApp != nullptr)
        {
            // [4] 线程结束时，释放在此线程中创建的数据库连接
            QObject::connect(QThread::currentThread(), &QThread::finished, qApp, [fullConnectionName]() {
                if (QSqlDatabase::contains(fullConnectionName))
                {
                    QSqlDatabase::removeDatabase(fullConnectionName);
                    qDebug().noquote() << QString("Connection deleted: %1").arg(fullConnectionName);
                }
            });
        }

        return createConnection(type.toUpper(), fullConnectionName);
    }
}


// 创建数据库连接
QSqlDatabase CDbConnectionPool::createConnection(const QString type, const QString &connectionName)
{
    static int sn = 0;

    // 创建一个新的数据库连接
    QSqlDatabase db;

    if (type == "LOCAL") {
        db = QSqlDatabase::addDatabase(d->databaseTypeLocal, connectionName);
        db.setHostName(d->hostNameLocal);
        db.setDatabaseName(d->databaseNameLocal);
        db.setUserName(d->usernameLocal);
        db.setPassword(d->passwordLocal);
        db.setPort(d->portLocal);
    } else if (type == "REMOTE") {
        db = QSqlDatabase::addDatabase(d->databaseTypeRemote, connectionName);
        db.setHostName(d->hostNameRemote);
        db.setDatabaseName(d->databaseNameRemote);
        db.setUserName(d->usernameRemote);
        db.setPassword(d->passwordRemote);
    }

    if (db.open())
    {
        qDebug().noquote() << QString("Connection created: %1, sn: %2").arg(connectionName).arg(++sn);
        return db;
    }
    else
    {
        qDebug().noquote() << "Create connection error:" << db.lastError().text();
        return QSqlDatabase();
    }
}
