/*
Copyright (c) 2006-2009, Tom Thielicke IT Solutions

SPDX-License-Identifier: GPL-2.0-only
*/

/****************************************************************
**
** File name: connection.h
**
****************************************************************/

#ifndef CONNECTION_H
#define CONNECTION_H

#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QList>
#include <QMessageBox>
#include <QSettings>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QStandardPaths>
#include <QString>
#include <QStringList>
#include <QTextStream>

#include "def/defines.h"
#include "sql/startsql.h"
#include "widget/errormessage.h"

// Database connection to SQLite
static bool createConnection()
{
    // Path do the database
    QString dbPath;
    // Filename of the template database
    QString dbNameTemplate = t10::app_db;
    // Filename of the user database
    QString dbNameUser = t10::app_user_db;
    // Connection to SQLite
    QSqlDatabase db;
    QList<QString> lessonId;
    QList<QString> lessonLesson;
    QList<QString> lessonTime;
    QList<QString> lessonToken;
    QList<QString> lessonStrokes;
    QList<QString> lessonErrors;
    QList<QString> lessonStamp;
    QList<QString> charUnicode;
    QList<QString> charTarget;
    QList<QString> charMistake;
    QList<QString> charOccur;

#if APP_PORTABLE
    auto dbDir = QCoreApplication::applicationDirPath() + "/portable/";
    dbPath = dbDir + dbNameUser;
#else
    QSettings settings;
    settings.beginGroup("database");
    QString oldDbPath = settings.value("pathpro", "").toString();
    settings.endGroup();

    auto dbDir
        = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    dbPath = dbDir + "/" + dbNameUser;

    if (QFile::exists(oldDbPath)) {
        QDir().mkpath(dbDir);
        QFile(oldDbPath).rename(dbPath);
        settings.beginGroup("database");
        settings.remove("pathpro");
        settings.endGroup();
    }
#endif

    // User path specified
    if (!QFile::exists(dbPath)) {
        // Try to create new databae in user path
        // Exist a database in the program dir?
        QString dbTemplatePath = t10::getDataDir() + dbNameTemplate;

        if (QFile::exists(dbTemplatePath)) {
            // A database exist in the program dir
            // -> copy database to appdata location
            QFile file(dbTemplatePath);

            QDir().mkpath(dbDir);
            if (file.copy(dbPath)) {
                QFile::setPermissions(
                    dbPath, QFile::permissions(dbPath) | QFile::WriteUser);
            } else {
                ErrorMessage* errorMessage = new ErrorMessage();
                errorMessage->showMessage(Error::sql_db_user_copy,
                    ErrorMessage::Type::Warning, ErrorMessage::Cancel::No,
                    QObject::tr("Affected directory:\n") + dbPath);
                return false;
            }
        } else {
            // No database found in program dir
            ErrorMessage* errorMessage = new ErrorMessage();
            errorMessage->showMessage(Error::sql_db_app_exist,
                ErrorMessage::Type::Critical, ErrorMessage::Cancel::Program,
                QObject::tr("Affected directory:\n") + dbPath);
            return false;
        }
    }

    if (QSqlDatabase::contains()) {
        db = QSqlDatabase::database();
        db.close();
    } else {
        db = QSqlDatabase::addDatabase("QSQLITE");
    }

    // Set database
    db.setDatabaseName(dbPath);
    // Open the database
    if (!db.open()) {
        // Error message
        ErrorMessage* errorMessage = new ErrorMessage();
        errorMessage->showMessage(Error::sql_connection,
            ErrorMessage::Type::Critical, ErrorMessage::Cancel::Program,
            QObject::tr("Affected directory:\n") + dbPath);
        return false;
    }

    QSqlQuery queryUpdate;

    if (!queryUpdate.exec("SELECT * FROM db_version ORDER BY version DESC;")) {
        // Error message
        ErrorMessage* errorMessage = new ErrorMessage();
        errorMessage->showMessage(Error::db_version_readable,
            ErrorMessage::Type::Critical, ErrorMessage::Cancel::Update);
    } else {
        if (!queryUpdate.first()) {
            // Error message
            ErrorMessage* errorMessage = new ErrorMessage();
            errorMessage->showMessage(Error::db_version_readable,
                ErrorMessage::Type::Critical, ErrorMessage::Cancel::Update);
        } else {
            // Server DB version is 0
            // -> software is too old to update
            if (queryUpdate.value(0).toInt() < t10::compiled_update_version) {

                // Update the database
                QStringList updateFileName = t10::update_url_sql.split("/");

                QFile sqlFile(
                    ":/update/" + updateFileName[updateFileName.count() - 1]);
                if (!sqlFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
                    // Error message
                    ErrorMessage* errorMessage = new ErrorMessage();
                    errorMessage->showMessage(Error::temp_file_creation,
                        ErrorMessage::Type::Critical,
                        ErrorMessage::Cancel::Update);
                }

                // Go to the beginning of the version file
                sqlFile.seek(0);

                QTextStream in(&sqlFile);

                QString line;

                // Execute all sql command of the downloaded file
                while (!in.atEnd()) {
                    line = in.readLine();
                    line = line.trimmed();
                    // Exclude comments and empty lines
                    if (line != ""
                        && !line.startsWith("//", Qt::CaseSensitive)) {
                        // Without error handling, because DROP-Statements are
                        // allowed to be invalid (there exist also a IF EXISTS
                        // statement in the SQLite library which suppresses an
                        // error, but it didn't work when I try it)
                        if (!queryUpdate.exec(line)
                            && !line.startsWith("drop", Qt::CaseInsensitive)) {
                            // Error message + failed sql string
                            ErrorMessage* errorMessage = new ErrorMessage();
                            errorMessage->showMessage(
                                Error::update_sql_execution,
                                ErrorMessage::Type::Critical,
                                ErrorMessage::Cancel::Update, line);
                            break;
                        }
                    }
                }
                StartSql* lessonSql = new StartSql();
                if (!lessonSql->analyzeLessons("lesson")) {
                    ErrorMessage* errorMessage = new ErrorMessage();
                    errorMessage->showMessage(Error::analyze_table_fill,
                        ErrorMessage::Type::Critical,
                        ErrorMessage::Cancel::Update, line);
                }
                if (!lessonSql->analyzeLessons("open")) {
                    ErrorMessage* errorMessage = new ErrorMessage();
                    errorMessage->showMessage(Error::analyze_table_fill,
                        ErrorMessage::Type::Critical,
                        ErrorMessage::Cancel::Update, line);
                }
                if (!lessonSql->analyzeLessons("own")) {
                    ErrorMessage* errorMessage = new ErrorMessage();
                    errorMessage->showMessage(Error::analyze_table_fill,
                        ErrorMessage::Type::Critical,
                        ErrorMessage::Cancel::Update, line);
                }
            }
        }
    }

    return true;
}

#endif // CONNECTION_H
