#include "utils.h"
#include "message_handler.h"
#include "git/git_client.h"


#if defined (Q_OS_UNIX) || defined (Q_OS_LINUX)
#include "unistd.h"
#endif

#include <QFile>
#include <QTextStream>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QRegularExpression>

#include <fstream>

int Utils::read1stLineOfFile(QString fileName) {
    QFile f(fileName);
    if (f.exists()) {
        if (f.open(QIODevice::ReadOnly | QIODevice::Text)) {
            QTextStream in(&f);
            in.setCodec("UTF-8");
            while(!in.atEnd()) {
                return in.readLine().toInt();
            }
        }
    }
    return -1;
}

QString Utils::zoneName(quint8 i) {
    static constexpr char const *zName[] = {
        "",
        "purple",
        "blue",
        "yellow",
        "green",
        "yellow (mars)",
        "green (mars)"
    };
    if (i < (sizeof(zName)/sizeof(char const *))) {
        return zName[i];
    }
    return "N/A";
}

void Utils::printCriticalErrorMsg(QString const &errorMsg, bool upper, bool lower) {
    if (upper) qCritical() << QString(80, 'E');

    qCritical() << errorMsg;

    if (lower) qCritical() << QString(80, 'E');
}

void Utils::printCriticalErrorMsg(QStringList const &errorMsg) {
    qCritical() << QString(80, 'E');
    for (int i = 0; i < errorMsg.size(); ++i) {
        qCritical() << errorMsg.at(i);
    }
    qCritical() << QString(80, 'E');
}

void Utils::printUpdateStatusMsg(QDebug debug, QStringList const &updateMsg) {
    //if (updateMsg.size() > 1) {
    //    qCritical() << QString(80, 'U');
    //}

    Q_UNUSED(debug);

    for (int i = 0; i < updateMsg.size(); ++i) {
        qInfo() << updateMsg.at(i);
    }

    //if (updateMsg.size() > 1) {
    //    qCritical() << QString(80, 'U');
    //}
}

void Utils::printUpdateStatusMsg(QStringList const &updateMsg) {
    //if (updateMsg.size() > 1) {
    //    qCritical() << QString(80, 'U');
    //}

    for (int i = 0; i < updateMsg.size(); ++i) {
        qCritical() << updateMsg.at(i);
    }

    //if (updateMsg.size() > 1) {
    //    qCritical() << QString(80, 'U');
    //}
}

void Utils::printUpdateStatusMsg(QString const &updateMsg, bool upper, bool lower) {
    if (upper) qCritical() << QString(80, 'U');

    qCritical() << updateMsg;

    if (lower) qCritical() << QString(80, 'U');
}

void Utils::printUpdateStatusMsg(QDebug debug, QString const &updateMsg,
                                 bool upper, bool lower) {
    if (upper) debug << QString(80, 'U');

    qInfo() << updateMsg;

    if (lower) debug << QString(80, 'U');

}

void Utils::printInfoMsg(QString const &infoMsg, bool upper, bool lower) {
    if (upper) qCritical() << QString(80, 'I');

    qCritical() << infoMsg;

    if (lower) qCritical() << QString(80, 'I');
}

void Utils::printInfoMsg(QStringList const &infoMsg) {
    //if (infoMsg.size() > 1) {
    //    qCritical() << QString(80, 'I');
    //}

    for (int i = 0; i < infoMsg.size(); ++i) {
        qCritical() << infoMsg.at(i);
    }

    //if (infoMsg.size() > 1) {
    //    qCritical() << QString(80, 'I');
    //}
}

void Utils::printLineEditInfo(QStringList const &lines) {
    if (getDebugLevel() == LOG_DEBUG) {
        for (int i=0; i<lines.size(); ++i) {
            qInfo() << lines.at(i);
        } qInfo() << ""; qInfo() << "";
    }
}

QString Utils::getTariffLoadTime(QString fileName) {
    QFileInfo fInfo(fileName);
    if (fInfo.exists()) {
        QDateTime lastModifiedTime = fInfo.lastModified();
        if (lastModifiedTime.isValid()) {
            return lastModifiedTime.toString(Qt::ISODateWithMs);
        } else {
            printCriticalErrorMsg(fileName + " HAS INVALID MODIFIED-TIME");
            QDateTime birthTime = fInfo.birthTime();
            if (birthTime.isValid()) {
                return birthTime.toString(Qt::ISODateWithMs);
            } else {
                printCriticalErrorMsg(fileName + " HAS INVALID BIRTH-TIME");
            }
        }
    } else {
        printCriticalErrorMsg(fileName + " DOES NOT EXIST");
    }
    return "N/A";
}

QString Utils::rstrip(QString const &str) {
    int n = str.size() - 1;
    for (; n >= 0; --n) {
        if (!str.at(n).isSpace()) {
            return str.left(n + 1);
        }
    }
    return "";
}

bool Utils::sameFilesInDirs(QDir const &dir1, QDir const &dir2,
                            QStringList const &nameFilters) {
    if (!dir1.exists()) {
        printCriticalErrorMsg(dir1.dirName() + " DOES NOT EXIST");
        return false;
    }
    if (!dir2.exists()) {
        printCriticalErrorMsg(dir2.dirName() + " DOES NOT EXIST");
        return false;
    }
    if (dir1.absolutePath() == dir2.absolutePath()) {
        printCriticalErrorMsg(dir1.dirName() + " AND "+ dir2.dirName() + " HAVE SAME PATH");
        return false;
    }

    // files, sorted by name
    QFileInfoList const &lst1 = dir1.entryInfoList(nameFilters, QDir::Files, QDir::Name);
    QFileInfoList const &lst2 = dir2.entryInfoList(nameFilters, QDir::Files, QDir::Name);

    QStringList fileNameLst1{};
    QStringList fileNameLst2{};
    QListIterator<QFileInfo> i1(lst1);
    while (i1.hasNext()) {
        fileNameLst1 << i1.next().fileName();
    }
    QListIterator<QFileInfo> i2(lst2);
    while (i2.hasNext()) {
        fileNameLst2 << i2.next().fileName();
    }

    if (fileNameLst1.isEmpty()) {
        qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
        return false;
    }
    if (fileNameLst2.isEmpty())  {
        qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
        return false;
    }
    if (fileNameLst1 != fileNameLst2) {
        printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
                            + " DIFFER: [" + fileNameLst1.join(',') + "],["
                            + fileNameLst2.join(',') + "]");
        return false;
    } else {
        printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
                    + " ARE EQUAL: [" + fileNameLst1.join(',') + "]");
    }

    QStringList gitBlobLst1{};
    QStringList gitBlobLst2{};
    QListIterator<QFileInfo> i3(lst1);
    while (i3.hasNext()) {
        gitBlobLst1 << GitClient::gitBlob(i3.next().fileName());
    }
    QListIterator<QFileInfo> i4(lst2);
    while (i4.hasNext()) {
        gitBlobLst2 << GitClient::gitBlob(i4.next().fileName());
    }

    if (gitBlobLst1.isEmpty()) {
        qCritical() << "DIR1" << dir1.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
        return false;
    }
    if (gitBlobLst2.isEmpty())  {
        qCritical() << "DIR1" << dir2.dirName() << " DOES NOT CONTAIN EXPECTED FILES";
        return false;
    }

    if (gitBlobLst1 != gitBlobLst2) {
        printCriticalErrorMsg(dir1.dirName() + " AND " + dir2.dirName()
                            + " DIFFER: [" + gitBlobLst1.join(',') + "],["
                            + gitBlobLst2.join(',') + "]");
        return false;
    } else {
        printInfoMsg(dir1.dirName() + " AND " + dir2.dirName()
                    + " CONTAIN SAME GIT-BLOBS FOR FILES: [" + fileNameLst1.join(',') + "]");

    }

    return true;
}


QString Utils::getParentName() { // get name of parent process
    QString ppid = QString("/proc/%1/status").arg(getppid());
    std::ifstream f(ppid.toStdString().c_str());
    if (f.is_open()) {
        std::string next;
        while (std::getline(f, next)) {
            QString line = QString(next.c_str()).simplified();
            if (line.startsWith("Name")) {
                int const idx = line.indexOf(QChar(':'));
                if (idx != -1) {
                    return line.mid(idx+1).trimmed();
                }
            }
        }
    }
    return "";
}

bool Utils::isATBQTRunning() {
    QDirIterator it("/proc",
                    QStringList() << "status",
                    QDir::Files,
                    QDirIterator::Subdirectories);
    while (it.hasNext()) {
        QString const &nextStatusFile = it.next();
        static const QRegularExpression re("^/proc/[0-9]{1,}/status");
        QRegularExpressionMatch match = re.match(nextStatusFile);
        if (match.hasMatch()) {
            std::ifstream f(nextStatusFile.toStdString().c_str());
            if (f.is_open()) {
                std::string next;
                while (std::getline(f, next)) {
                    QString line = QString(next.c_str()).simplified();
                    if (line.startsWith("Name")) {
                        int const idx = line.indexOf(QChar(':'));
                        if (idx != -1) {
                            QString const binary = line.mid(idx+1).trimmed();
                            if (binary == "ATBQT") {
                                return true;
                            }
                        }
                    }
                }
            }
        }
    }
    return false;
}