#include "database.h"
#include <QStandardPaths>
#include <QDir>
#include <QSqlError>
#include <QDebug>
#include <QDateTime>
#include "loadrunable.h"

Database::Database(QObject *parent) : QObject(parent)
{
}

bool Database::init()
{
    QString path = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
    QDir dir(path);

    QSqlDatabase m_database = QSqlDatabase::addDatabase("QSQLITE");

    if(!dir.mkpath("."))
        return false;

    if(m_database.isValid())
    {
        m_database.setDatabaseName(dir.absoluteFilePath("database.db"));
        if(m_database.open())
        {
            m_database.exec("PRAGMA foreign_keys = ON");
            m_database.exec("CREATE TABLE IF NOT EXISTS files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE)");
            m_database.exec("CREATE TABLE IF NOT EXISTS fits_files (id INTEGER PRIMARY KEY AUTOINCREMENT, file VARCHAR(255) UNIQUE, mtime DATETIME)");
            m_database.exec("CREATE TABLE IF NOT EXISTS fits_headers (id INTEGER PRIMARY KEY AUTOINCREMENT, id_file INTEGER,"
                            "key VARCHAR(81), value VARCHAR(81), comment VARCHAR(81), FOREIGN KEY(id_file) REFERENCES fits_files(id) ON DELETE CASCADE)");
            m_database.exec("CREATE INDEX IF NOT EXISTS key_value ON fits_headers(key, value)");
            m_database.exec("CREATE INDEX IF NOT EXISTS id_file ON fits_headers(id_file)");
            QSqlError error = m_database.lastError();

            if(error.type() == QSqlError::NoError)
            {
                m_markQuery = QSqlQuery(m_database);
                m_markQuery.prepare("INSERT INTO files (file) VALUES (?)");
                m_unmarkQuery = QSqlQuery(m_database);
                m_unmarkQuery.prepare("DELETE FROM files WHERE file = (?)");
                m_isMarkedQuery = QSqlQuery(m_database);
                m_isMarkedQuery.prepare("SELECT * FROM files WHERE file = (:name)");

                m_insertFile = QSqlQuery(m_database);
                m_insertFile.prepare("INSERT INTO fits_files (file, mtime) VALUES (?, ?)");
                m_insertFitsHeader = QSqlQuery(m_database);
                m_insertFitsHeader.prepare("INSERT INTO fits_headers (id_file, key, value, comment) VALUES (?, ?, ?, ?)");
                m_checkFile = QSqlQuery(m_database);
                m_checkFile.prepare("SELECT id,mtime FROM fits_files WHERE file=?");
                m_headerKeywords = QSqlQuery(m_database);
                m_headerKeywords.prepare("SELECT DISTINCT key FROM fits_headers ORDER BY key");
                m_deleteFile = QSqlQuery(m_database);
                m_deleteFile.prepare("DELETE FROM fits_files WHERE id=?");
                return true;
            }

            qDebug() << error.text();
        }
    }
    return false;
}

bool Database::mark(const QString &filename)
{
    m_markQuery.bindValue(0, filename);
    m_markQuery.exec();
    return checkError(m_markQuery);
}

bool Database::unmark(const QString &filename)
{
    m_unmarkQuery.bindValue(0, filename);
    m_unmarkQuery.exec();
    return checkError(m_unmarkQuery);
}

bool Database::isMarked(const QString &filename)
{
    m_isMarkedQuery.bindValue(":name", filename);
    m_isMarkedQuery.exec();
    checkError(m_isMarkedQuery);
    return m_isMarkedQuery.next();
}

QStringList Database::getMarkedFiles()
{
    QSqlQuery markedFiles("SELECT * from files");

    QStringList files;
    while(markedFiles.next())
    {
        files << markedFiles.value("file").toString();
    }

    qDebug() << files.size();
    return files;
}

bool Database::checkError(QSqlQuery &query)
{
    QSqlError error = query.lastError();
    if(error.type() == QSqlError::NoError)
        return true;
    else
    {
        qDebug() << error.text();
        return false;
    }
}

static QStringList nameFilters = {"*.fit", "*.fits", "*.xisf"};

static int countFiles(const QDir &dir, int count = 0)
{
    count += dir.entryList(nameFilters, QDir::Files).size();
    QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    for(const QString &d : dirs)
        count += countFiles(dir.filePath(d));
    return count;
}

void Database::indexDir(const QDir &dir, QProgressDialog *progress)
{
    m_progress = 0;
    int count = countFiles(dir);
    progress->setMaximum(count);
    QSqlDatabase database = QSqlDatabase::database();
    database.transaction();
    if(indexDir2(dir, progress))
        database.commit();
    else
        database.rollback();
}

QStringList Database::getFitsKeywords()
{
    m_headerKeywords.exec();
    QStringList keywords;
    while(m_headerKeywords.next())
    {
        keywords << m_headerKeywords.value(0).toString();
    }
    return keywords;
}


bool Database::indexDir2(const QDir &dir, QProgressDialog *progress)
{
    QFileInfoList files = dir.entryInfoList(nameFilters, QDir::Files);
    QStringList dirs = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);

    for(const QString &d : dirs)
    {
        if(!indexDir2(dir.filePath(d), progress))
            return false;
    }
    ImageInfoData info;
    for(const QFileInfo &file : files)
    {
        progress->setValue(m_progress++);
        if(progress->wasCanceled())return false;
        QString filePath = file.absoluteFilePath();
        QString mtime = file.fileTime(QFileDevice::FileModificationTime).toUTC().toString(Qt::ISODate);
        m_checkFile.bindValue(0, filePath);
        m_checkFile.exec();
        if(m_checkFile.next())
        {
            if(m_checkFile.value(1).toString() == mtime)
                continue;
            else
            {
                m_deleteFile.bindValue(0, m_checkFile.value(0).toLongLong());
                m_deleteFile.exec();
            }
        }

        bool ok;
        if(filePath.endsWith(".xisf", Qt::CaseInsensitive))
            ok = readXISFHeader(filePath, info);
        else
            ok = readFITSHeader(filePath, info);

        qlonglong last_id = -1;
        if(ok)
        {
            m_insertFile.bindValue(0, filePath);
            m_insertFile.bindValue(1, mtime);
            if(!m_insertFile.exec())
            {
                qDebug() << m_insertFile.lastError();
                return false;
            }
            last_id = m_insertFile.lastInsertId().toLongLong();
            QVariantList file_id, keys, values, comments;
            for(auto &record : info.fitsHeader)
            {
                file_id << last_id;
                keys << QString(record.key);
                values << record.value.toString();
                comments << QString(record.comment);
            }
            m_insertFitsHeader.bindValue(0, file_id);
            m_insertFitsHeader.bindValue(1, keys);
            m_insertFitsHeader.bindValue(2, values);
            m_insertFitsHeader.bindValue(3, comments);
            if(!m_insertFitsHeader.execBatch())
            {
                qDebug() << m_insertFitsHeader.lastError();
                return false;
            }
        }
        qDebug() << filePath << last_id;
        info.fitsHeader.clear();
    }
    return true;
}
