#include "fasthashcalculator.h"
#include <QtConcurrent>


FastHashCalculator::FastHashCalculator(QObject *parent) : QObject(parent)
{
    hashList = new QList<FileData>();
}
void FastHashCalculator::calculateHashes(const QString& path, const QString& ignoreName)
{
    hashList->clear();
    currentSize = 0;
    if(!QDir(path).exists()){
        QDir().mkdir(path);
    }

    QString hashString;
    QStringList filter;
    filter << "*";
    QList<FileData> *folders = new QList<FileData>;
    fullSize = Tools::convertFileSize(getDirectorySize(path),false);

    QDirIterator dirIterator(path,filter, QDir::AllEntries, QDirIterator::Subdirectories);

    while (dirIterator.hasNext())
    {
        QFileInfo fileInfo(dirIterator.next());
        FileData currentFolder;
        if(fileInfo.isDir() && !fileInfo.fileName().startsWith(".") && fileInfo.fileName() != "RRJLoader")
        {
            currentFolder.path = Tools::createLocalPath(fileInfo.absoluteFilePath());
            currentFolder.hash = "FOLDER";

            if(!folders->contains(currentFolder))
            {
                folders->push_back(currentFolder);
            }
        }
    }


    QDirIterator fileIterator(path,filter,QDir::Files | QDir::NoDotAndDotDot,QDirIterator::Subdirectories);

    QList<QString> files;
    files.clear();

    while(fileIterator.hasNext())
    {
        fileIterator.next();
        QFileInfo fileInfo = fileIterator.fileInfo();
        QString path = fileInfo.absoluteFilePath();

        if (fileInfo.isHidden()) continue;
        if (!fileInfo.isFile()) continue;
        if (fileInfo.fileName().contains(".meta")) continue;
        if (ignoreName != "" && fileInfo.path().contains(ignoreName)) continue;

        files.append(path);
    }

    QtConcurrent::map(files, [this](const QString &filePath)
    {
        QByteArray hash = calculateFileHashOptimized(filePath);
        QMutexLocker locker(&_mutex);
        FileData currentFile;
        QString hashName;

        currentFile.path = Tools::createLocalPath(filePath);
        currentFile.hash = hash.toHex();
        hashList->append(currentFile);
     }).waitForFinished();

    hashList->append(*folders);
    emit finished();
}

QByteArray FastHashCalculator::calculateFileHashOptimized(const QString &filePath)
{
    QFile file(filePath);
            if (!file.open(QIODevice::ReadOnly)) return QByteArray();

            QCryptographicHash hash(QCryptographicHash::Md5);
            const qint64 bufferSize = 2048; // 2MB
            quint64 completeBytes = 0;
            QByteArray buffer;
            buffer.resize(bufferSize);

            while (!file.atEnd()) {
                qint64 bytesRead = file.read(buffer.data(), bufferSize);
                hash.addData(buffer.constData(), bytesRead);
                completeBytes += bytesRead;
            }

            hashCounterDisplay(completeBytes);
            return hash.result();
}

void FastHashCalculator::hashCounterDisplay(quint64 size)
{
    currentSize += size;
     emit sigSendHashInfo(fullSize,Tools::convertFileSize(currentSize,false));
}

quint64 FastHashCalculator::getDirectorySize(const QString& path)
{
    quint64 totalSize = 0;
    QDirIterator iterator(path, QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);

    while (iterator.hasNext())
    {
        iterator.next();
        QFileInfo fileInfo = iterator.fileInfo();
        if (fileInfo.isFile())
        {
            totalSize += fileInfo.size();
        }
    }

    return totalSize;
}

QList<FileData> *FastHashCalculator::getHashList() const
{
    return hashList;
}
