#include "updatecontroller.h"

void UpdateController::initialize(TCPServer *server,DataParser *dataParser)
{
    this->server = server;
    this->dataParser = dataParser;
    sizeToSend = 0;
    mutex = new QMutex;
}

UpdateController::UpdateController(QObject *parent) : QObject(parent)
{
    sourcePath = QDir::currentPath() + "/" + applicationFolderName;
    emit sigLogMessage(sourcePath);
    qDebug() << hashFileName;

    if(!QFile::exists(hashFileName)){
        calculateHash();
    }else{
        loadHash();
    }

}

void UpdateController::compareFiles(ClientHandler* handler, QByteArray array)
{
    mutex->lock();
    loadHash();
    clientDataList.clear();
    dataParser->xmlFileDataParse(array);
    clientDataList.append(*dataParser->getDatas());

    //Вывод информации о доступных файлов/*
    //    QListIterator<FileData> iterator(clientDataList);
    //    while(iterator.hasNext()){
    //        auto data = iterator.next();
    //        emit sigLogMessage(data.path);
    //        emit sigLogMessage(data.hash);
    //        emit sigLogMessage("____________");
    //    }*/

    checkNeedUpdate(handler);
    mutex->unlock();
}


void UpdateController::showHash()
{
    for(FileData& str : serverDataList){
        emit sigLogMessage(str.hash);
    }
}

void UpdateController::saveHash()
{
    QFile hashFile(hashFileName);
    hashFile.open(QIODevice::WriteOnly);
    QXmlStreamWriter xmlWriter(&hashFile);

    xmlWriter.setAutoFormatting(true);
    xmlWriter.writeStartDocument();
    xmlWriter.writeStartElement("FileDataList");

    foreach (FileData data,serverDataList)
    {
        xmlWriter.writeStartElement("FileData");

        xmlWriter.writeAttribute("Path",data.path);
        xmlWriter.writeAttribute("Hash",data.hash);

        xmlWriter.writeEndElement();
    }

    xmlWriter.writeEndElement();
    xmlWriter.writeEndDocument();

    hashFile.close();

}

void UpdateController::loadHash()
{
    serverDataList.clear();

    QFile hashFile(hashFileName);
    QByteArray array;
    if(hashFile.open(QIODevice::ReadOnly)){
        array = hashFile.readAll();
        hashFile.close();
    }

    QXmlStreamReader xmlReader(array);

    while (!xmlReader.atEnd())
    {
        if(xmlReader.isStartElement())
        {
            if(xmlReader.name().toUtf8() == "FileData")
            {
                FileData data;

                foreach(const QXmlStreamAttribute &attr,xmlReader.attributes())
                {
                    QString name = attr.name().toString();
                    QString value = attr.value().toString();

                    if(name == "Path")
                        data.path = value;
                    else if(name == "Hash")
                        data.hash = value;
                }

                serverDataList.append(data);
            }

        }

        xmlReader.readNextStartElement();
    }

    emit sigLogMessage("Hash load from file ");

}

void UpdateController::calculateSize()
{
    QDirIterator iterator(sourcePath);
    quint64 total = 0;

    while(iterator.hasNext()){
        if(iterator.fileInfo().isFile()){
            total += iterator.fileInfo().size();
        }

        iterator.next();
    }

    emit sigLogMessage("Full size: ");
    emit sigLogMessage(QString::number(total));
}

QString UpdateController::getCommands()
{
    QString commandsText;
    commandsText += "check - check version ";
    commandsText += "update - update files ";

    return commandsText;

}

void UpdateController::setLocalFileData(QList<FileData> dataList)
{
    serverDataList.append(dataList);
}

bool UpdateController::checkNeedUpdate(ClientHandler *handler)
{
    QList<FileData> *forSend = new QList<FileData>;
    QList<FileData> *forDelete = new QList<FileData>;

    fileSendList.clear();
    fileDeleteList.clear();
    sizeToSend = 0;

    bool needUpdate = false;

    for (auto &item:clientDataList)                 //проверка на недостающие файлы по адресам
    {
        if(item.path.contains("Temp")) continue;

        if (!serverDataList.contains(item))
        {
            forDelete->append(item);
        }
    }

    for (auto &item:serverDataList)
    {
        if(item.path.contains("Temp")) continue;

        if (!clientDataList.contains(item))
        {
            forSend->append(item);
        }
    }
//    for (auto &item:clientDataList)                 //проверка на недостающие файлы по адресам
//    {
//        if (!serverDataList.contains(item))
//        {
//            serverDataList.removeOne(item);
//            clientDataList.removeOne(item);
//        }
//    }

//    for (auto &item:clientDataList)                 //проверка на недостающие файлы по адресам
//    {
//        if(item.path.contains("Temp")){
//            clientDataList.removeOne(item);
//        }
//    }

//    QListIterator<FileData> serverDiffIterator(serverDataList);

//    while (serverDiffIterator.hasNext())            //добавление недостающих файлов в список для отправки
//    {
//        auto fileForUpdate = serverDiffIterator.next();

//        if(fileForUpdate.path.contains("Temp")) continue;

//        fileSendList.append(fileForUpdate);
//    }

    if(forSend->length() > 0)                   //формирование сообщения об обновлении
    {
        QString log;
        log.append(Tools::getTime());
        log.append(" Client: " + handler->getClient()->getLogin());
        log.append(" Need updates: ");
        log.append(QString::number(forSend->length()));
        log.append(" objects");
        fileSendList = *forSend;
        emit sigLogMessage(log);
        handler->sendMessageBlock(log);
        needUpdate = true;
    }
    else
    {
        QString log;
        log.append(Tools::getTime());
        log.append(" Client: " + handler->getClient()->getLogin());
        log.append(" no update required");
        emit sigLogMessage(log);
        handler->sendMessageBlock(log);
    }

    if(forDelete->length() > 0){
        QString log;
        log.append(Tools::getTime());
        log.append(" Client: " + handler->getClient()->getLogin());

        log.append(" Need delete: ");
        log.append(QString::number(forDelete->length()));
        log.append(" objects");
        //ОПАСНОСТЬ С РУКИ! УДАЛИТЬ И ПРИВЕСТИ В ПОРЯДОК
        clientDataList = *forDelete;
        //
        emit sigLogMessage(log);
        handler->sendMessageBlock(log);
        needUpdate = true;
    }
    else
    {
        QString log;
        log.append(Tools::getTime());
        log.append(" Client: " + handler->getClient()->getLogin());
        log.append(" no delete required");
        emit sigLogMessage(log);
        handler->sendMessageBlock(log);
    }

    CalculateSizeToSend(*forSend);
    handler->sendNeedUpdate(needUpdate,sizeToSend,forSend->length());

    return needUpdate;
}

void UpdateController::calculateHash()
{

    QFile file(hashFileName);
    serverDataList.clear();

    if(file.exists()){
        file.remove();
    }

    QDirIterator iterator(sourcePath,QDirIterator::Subdirectories);

    if(!QDir(applicationFolderName).exists()){
        QDir().mkdir(applicationFolderName);
    }

    QDir dir(sourcePath);
    QString hashString;
    QList<FileData> *files = new QList<FileData>;

    while (iterator.hasNext())
    {
        iterator.next();
        QFileInfo fileInfo = iterator.fileInfo();
        FileData currentFile;
        QFile file(fileInfo.absoluteFilePath());

        quint64 fileSize = file.size();
        const quint64 bufferSize = 10240;

        if (fileInfo.isHidden()) continue;

        if(fileInfo.isFile() && file.open(QIODevice::ReadOnly) && !fileInfo.fileName().contains(".meta"))
        {
            char buffer[bufferSize];
            int bytesRead;
            int readSize = qMin(fileSize,bufferSize);

            QCryptographicHash hash(QCryptographicHash::Md5);
            while(readSize > 0 && (bytesRead = file.read(buffer,readSize)) > 0){
                fileSize -= bytesRead;
                hash.addData(buffer,bytesRead);
                readSize = qMin(fileSize,bufferSize);
            }

            hashString = QString(hash.result().toHex());
            currentFile.hash = hashString;
            currentFile.path = Tools::createLocalPath(fileInfo.absoluteFilePath());

            files->push_back(currentFile);
            file.close();

        }
        else if(fileInfo.isDir() && fileInfo.fileName() != ".." && !fileInfo.isRoot())
        {
            currentFile.hash = "FOLDER";
            currentFile.path = Tools::createLocalPath(fileInfo.path());

            if(!folderDataList.contains(currentFile)){
                folderDataList.push_back(currentFile);
            }

        }
    }

    emit sigLogMessage("Calculate hash complete");
    serverDataList.append(folderDataList);
    serverDataList.append(*files);

    saveHash();

    qDebug() << "Recalculate complete";
    delete files;
}

QByteArray UpdateController::getLocalHash()
{
    QFile hashFile(hashFileName);
    QByteArray array;
    if(hashFile.open(QIODevice::ReadOnly)){
        array = hashFile.readAll();
        hashFile.close();
    }

    return array;
}

void UpdateController::CalculateSizeToSend(QList<FileData> diffList)
{
    QListIterator<FileData> serverDiffIterator(diffList);

    while (serverDiffIterator.hasNext())            //добавление недостающих файлов в список для отправки
    {
        auto pathForUpdate = serverDiffIterator.next();
        QString path = Tools::createRootPath(pathForUpdate.path);
        QFile file(path);

        sizeToSend += file.size();
    }
}

UpdateController::~UpdateController()
{

}

QList<FileData> UpdateController::getFileSendList() const
{
    return fileSendList;
}


QList<FileData> UpdateController::getClientDataList() const
{
    return clientDataList;
}

