#include "recognizesystem.h"

RecognizeSystem::RecognizeSystem(QObject *parent):
QObject(parent)
{
    packetType = PacketType::TYPE_NONE;
    filePath.clear();
    fileSize = 0;
    sizeReceiveData = 0;
    tmpBlock.clear();
    countSend = 0;
    mutex = new QMutex;

}

void RecognizeSystem::initialize(UpdateController *updateController,DataParser* dataParser,
    ServerLMSWidget *server,SendSystem *sendSystem, ClientHandler *handler)
{
    this->updateController = updateController;
    this->dataParser = dataParser;
    this->server = server;
    this->clientHandler = handler;
    this->sendSystem = sendSystem;
    socket = handler->getSocket();

    connect(this,&RecognizeSystem::sigCalculateHash,updateController,&UpdateController::calculateFullHashWithSetup,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigChangeVersion,updateController,&UpdateController::changeAssetVersion,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigDeleteVersion,updateController,&UpdateController::deleteAssetVersion,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigCopyVersion,updateController,&UpdateController::createCopyVersion,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigXmlParser,dataParser->getProcessParser(),&ProcessParser::slot_read,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigRecalculateDocs,server,&ServerLMSWidget::slot_UpdateDocs,Qt::AutoConnection);
    connect(this,&RecognizeSystem::sigSendDocs,sendSystem,&SendSystem::sendDocs,Qt::AutoConnection);

    qDebug() << "Recognize init thread ID " << QThread::currentThreadId();
}

void RecognizeSystem::recognize()
{
     QMutexLocker locker(mutex);
    qDebug() << "Recognize thread ID " << QThread::currentThreadId();
    QDataStream stream(socket);
    stream.setVersion(QDataStream::Qt_DefaultCompiledVersion);
    QByteArray data;
    Client *client = clientHandler->getClient();

    while (socket->bytesAvailable() > 0)
    {

        if (packetType == PacketType::TYPE_NONE)
        {
            stream.startTransaction();

            if(client->getClientType() == TypeClientAutorization::TYPE_UNITY_CLIENT)
            {
                char *read = new char[4];
                stream.readRawData(read,4);
                int numPackage = *((int*)read);
                packetType = static_cast<PacketType>(numPackage);
                socket->peek(read,4);
                if(!stream.commitTransaction()) continue;
                continue;
            }
            else
            {
                stream >> packetType;

                if(!stream.commitTransaction()) continue;
            }

        }

        if (packetType != PacketType::TYPE_NONE)
               {
                   QString result = enumToString(packetType);
                   Logger::instance().log("RECEIVE FROM: " + client->getLogin() + " " +
                                          result ,LogLevel::DEBUG);
                   //секция на случай прихода неизвестного пакета
                   if(result == "Unknown")
                   {
                           qDebug() << "  State:" << socket->state();
                           qDebug() << "  Error:" << socket->error();
                           qDebug() << "  Bytes to write:" << socket->bytesToWrite();
                           qDebug() << "  Bytes available:" << socket->bytesAvailable();
                       if(client->getTypeClient() == TypeClientAutorization::TYPE_UNITY_CLIENT)
                       {
                           data = socket->readAll();
                       }
                       else
                       {
                           stream.startTransaction();
                           stream >> data;
                       }
                       QString dataText = QString::fromUtf8(data);
                       Logger::instance().log(QString::number(packetType) + " Unknown text " + dataText);
                       Logger::instance().log("Client error: " + client->getLogin(),LogLevel::ERROR);
                       clientHandler->sendDisable();
                       //mutex->unlock();
                   }
               }

        if (packetType == PacketType::TYPE_COMMAND) //TODO: надо переделать под какой то нормальный тип, который уже существует
        {
            stream.startTransaction();
            stream >> command;

            if (!stream.commitTransaction()) continue;
        }

        if (packetType == PacketType::TYPE_UPDATE)
        {
            QList<FileData> sendList = updateController->prepareRealPathList(client->getFileSendList());
            sendSystem->updateFiles(sendList,
                                    client->getFileDeleteList());

            qDebug()<< "Call update no docs";
            packetType = PacketType::TYPE_NONE;
        }

        if(packetType == PacketType::TYPE_CHECK_VERSION)
        {
            updateController->compareFiles(clientHandler,client->getClientHash());
        }

        if (packetType == PacketType::TYPE_XMLANSWER)
        {

            if(clientHandler->getClient()->getClientType() == TypeClientAutorization::TYPE_UNITY_CLIENT)
            {
                data = socket->readAll();
            }
            else
            {
                stream.startTransaction();
                stream >> data;

                if(!stream.commitTransaction()) continue;
            }

            qDebug() << data;
            emit sigXmlParser(clientHandler,data);
            Logger::instance().log(" Text " + QString(data), LogLevel::DEBUG);
            packetType = PacketType::TYPE_NONE;
            continue;
        }

        if(packetType == PacketType::TYPE_BIGXML)
        {
            if (clientHandler->getClient()->getClientType() == TypeClientAutorization::TYPE_UNITY_CLIENT)
            {
                char *readBuffer = new char[4];
                socket->read(readBuffer,4);
                fileSize = *((int*)readBuffer);

                //if(!stream.commitTransaction()) continue;
                //ПОЛУЧЕНИЕ САМОГО ФАЙЛА
                Logger::instance().log("AfterRead size and path BytesAvailable: " + QString::number(socket->bytesAvailable()));

                qint64 readSize = readFileBlockSize;
                forever
                {

                    if(fileSize < readSize)
                    {
                        readSize = fileSize;
                        Logger::instance().log("LastPackage: " + QString::number(readSize));
                    }

                    socket->waitForReadyRead(20);
                    tmpBlock = socket->read(readSize);

                    data.append(tmpBlock);

                    fileSize -= readSize;
                    sizeReceiveData += readSize;
                    countSend++;

                    tmpBlock.clear();

                    if(fileSize == 0) break;
                }

                Logger::instance().log("File loaded");

                 emit sigXmlParser(clientHandler,data);

                //ОЧИСТКА ПОСЛЕ ПЕРЕДАЧИ
                fileSize = 0;
                tmpBlock.clear();
                sizeReceiveData = 0;
                countSend = 0;
                packetType = PacketType::TYPE_NONE;
                data.clear();
                continue;

            }
        }

        if(packetType == PacketType::TYPE_FOLDER)   //создание папок
        {
            if(client->getClientType() == TypeClientAutorization::TYPE_UNITY_CLIENT)
            {
                filePath = socket->readAll();
                filePath = Tools::createSharedPath(filePath);
            }
            else
            {
                stream.startTransaction();
                stream >> filePath;

                if(!stream.commitTransaction()){
                    continue;
                }

                filePath = Tools::createFullPath(filePath);

            }

            QDir dir(filePath);
            if(!dir.exists()){
                if(dir.mkpath(filePath)){
                    qDebug() << "Dir Created";
                }
            }

            packetType = PacketType::TYPE_NONE;
            continue;
        }

        if (packetType == PacketType::TYPE_FILE)  //выгрузка одного файла
        {

            if(client->getClientType() == TypeClientAutorization::TYPE_UNITY_CLIENT)
            {
                DataInfo *currentFileData = updateController->getCurrentDataInfo();
                filePath = currentFileData->path;
                filePath = Tools::createSharedPath(filePath);

                QFile file(filePath);
                if (file.exists())
                {
                    file.remove();  //удаление файла, если он уже есть, но необходимо обновить
                   Logger::instance().log("Delete exist file: " + filePath);
                }

                if (!file.open(QFile::Append))
                {
                    QString folderPath = Tools::createFolderPath(filePath);
                    qDebug() << "FULL PATH: " << folderPath;
                    QDir dir(folderPath);
                    if (!dir.exists()){
                        dir.mkpath(".");
                    }

                    file.open(QFile::Append);
                }

                //socket->waitForReadyRead(1000);

                fileSize = currentFileData->size;

                qint64 readSize = 65535;
                forever
                {
                    if(fileSize < readSize)
                    {
                        readSize = fileSize;
                        qDebug() << "LastPackage: " << readSize;
                    }

                    socket->waitForReadyRead(20);
                    tmpBlock = socket->read(readSize);

                    quint64 toFile = file.write(tmpBlock);
                    fileSize -= toFile;
                    sizeReceiveData += toFile;
                    countSend++;

                    tmpBlock.clear();
                    qDebug() << "Loading progress: " << fileSize << "/" << currentFileData->size;

                    if(fileSize == 0) break;

                }

                file.close();
                filePath.clear();
                updateController->clearCurrentDataInfo();

                socket->waitForReadyRead(100);

                packetType = PacketType::TYPE_NONE;

            }
            else
            {
                forever
                {
                    stream.startTransaction();
                    stream >> filePath;
                    stream >> fileSize;

                    if(!stream.commitTransaction()) continue;

                    filePath = Tools::createFullPath(filePath);
                    socket->waitForReadyRead(100);
                    break;
                }

                QFile file(filePath);
                QFileInfo fileInfo(file);

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

                file.open(QFile::Append);

                forever
                {
                    stream.startTransaction();
                    stream >> tmpBlock;

                    if(!stream.commitTransaction()){

                        if(socket->state() == QAbstractSocket::UnconnectedState){
                            qDebug() << "UNCONNECT";
                            //mutex->unlock();
                            file.close();
                            return;
                        }
                        if(socket->waitForReadyRead(100)){
                            continue;
                        }

                        continue;
                    }

                     quint64 toFile = file.write(tmpBlock);
                     sizeReceiveData += toFile;
                     countSend++;

                     tmpBlock.clear();

                     if(sizeReceiveData == fileSize) break;


                }

                QString logMessage = "Load from " + client->getLogin() + " ";
                logMessage.append(fileInfo.fileName());

               Logger::instance().log(logMessage);

                file.close();

                filePath.clear();
                fileSize = 0;
                tmpBlock.clear();
                sizeReceiveData = 0;
                countSend = 0;
                packetType = PacketType::TYPE_NONE;
            }

        }

        if (packetType == PacketType::TYPE_FILESIZE)
        {
            fileSize = socket->readAll().toULong();
            qDebug() << fileSize;
            packetType = PacketType::TYPE_NONE;
            continue;
        }

        if(packetType == PacketType::CHANGE_DATA_VERSION)
        {
            stream.startTransaction();
            QString versionName;
            stream >> versionName;

            if(!stream.commitTransaction()) continue;

            qDebug() << "For change " + versionName;

            emit sigChangeVersion(versionName);

        }

        if(packetType == PacketType::COPY_VERSION)
        {
            QString source;
            QStringList result;

            stream.startTransaction();
            stream >> source;

            if(!stream.commitTransaction()) continue;

            result = source.split(";");

            if (updateController->checkDuplicate(result[1]))
            {
                sendSystem->sendNotify(commandDuplicateVerName);
                packetType = PacketType::TYPE_NONE;
                break;
            }

            emit sigCopyVersion(result[0],result[1],result[2]);
            sendSystem->sendPacketType(PacketType::BUSY);
        }

        if(packetType == PacketType::DELETE_DATA_VERSION)
        {
            stream.startTransaction();
            QString versionName;
            stream >> versionName;

            if(!stream.commitTransaction()) continue;

            qDebug() << "For delete " + versionName;


            if (versionName == baseNameVersion)
            {
                sendSystem->sendNotify(commandTryBaseDelete);
            }
            else if (versionName == updateController->getCurrentVersionName())
            {
                sendSystem->sendNotify(commandTryActiveDelete);
            }
            else
            {
                emit sigDeleteVersion(versionName);
            }

        }

        if(packetType == PacketType::TYPE_DISABLE)
        {
            clientHandler->sendDisable();            
        }

        if(packetType == PacketType::RECALCULATE_DOCS)
        {
            emit sigCalculateHash();
            emit sigRecalculateDocs();
        }

        if(packetType == PacketType::GET_DOCS)
        {
            emit sigSendDocs(updateController->getPathAdditionalFile(tasksAMMfileName));
        }

        if (packetType == PacketType::SEND_HASH)
        {
            QByteArray hash;

            forever
            {
                stream.startTransaction();
                stream >> fileSize;

                if(!stream.commitTransaction()) continue;
                socket->waitForReadyRead(100);
                break;
            }

            forever
            {
                stream.startTransaction();
                stream >> tmpBlock;

                if(!stream.commitTransaction()){

                    if(socket->state() == QAbstractSocket::UnconnectedState){
                        qDebug() << "UNCONNECT";
                        //mutex->unlock();
                        return;
                    }
                    if(socket->waitForReadyRead(100)){
                        continue;
                    }

                    continue;
                }

                 hash += tmpBlock;
                 sizeReceiveData += tmpBlock.length();
                 countSend++;

                 tmpBlock.clear();

                 if(sizeReceiveData == fileSize) break;


            }

            QString logMessage = "Load from " + client->getLogin() + " ";

            filePath.clear();
            fileSize = 0;
            tmpBlock.clear();
            sizeReceiveData = 0;
            countSend = 0;
            packetType = PacketType::TYPE_NONE;
            client->setClientHash(hash);
        }
        packetType = PacketType::TYPE_NONE;
    }

    //mutex->unlock();
}

bool RecognizeSystem::checkIsChangeable()
{
    return updateController->getCurrentVersion()->getIsChangeable();
}

RecognizeSystem::~RecognizeSystem()
{

}
