/********************************************************************************************
 *                                                                                          *
 * Copyright (C) 2017 Armin Felder, Dennis Beier                                            *
 * This file is part of RocketChatMobileEngine <https://git.fairkom.net/chat/fairchat>.     *
 *                                                                                          *
 * RocketChatMobileEngine is free software: you can redistribute it and/or modify           *
 * it under the terms of the GNU General Public License as published by                     *
 * the Free Software Foundation, either version 3 of the License, or                        *
 * (at your option) any later version.                                                      *
 *                                                                                          *
 * RocketChatMobileEngine is distributed in the hope that it will be useful,                *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of                           *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                            *
 * GNU General Public License for more details.                                             *
 *                                                                                          *
 * You should have received a copy of the GNU General Public License                        *
 * along with RocketChatMobileEngine. If not, see <http://www.gnu.org/licenses/>.           *
 *                                                                                          *
 ********************************************************************************************/



#include "fileuploader.h"

FileUploader::FileUploader( QObject *parent, RocketChatServerData *pServer, const QUrl &pUri, const QString &pChannelId ): QObject( parent ),
    mUri( pUri ), mChannelId( pChannelId ), mServer( pServer )
{
    auto file = QSharedPointer<QFile>::create( pUri.toLocalFile() );

    if ( file->isOpen() ) {
        file->close();
    }

    if ( file->open( QIODevice::ReadOnly ) ) {
        mDump = file->readAll();
        this->mSize = mDump.size();
        qDebug() << "read file with bytes:" << file->size();

        if ( !this->mSize ) {
            mErrorStatus = true;
        } else {
            file->close();
            QMimeDatabase db;
            mType = db.mimeTypeForFile( pUri.fileName() );
        }
    } else {
        qWarning() << file->errorString();
    }
}

void FileUploader::upload( const std::function<void( void )> &pCleanup )
{
    this->mCleanUp = pCleanup;
    DdpCallback success = [ & ]( QJsonObject response, MeteorDDP * ddp ) {
        Q_UNUSED( ddp );
        onUfsCreated( response );
    };
    DdpCallback errorFunc = [ & ]( QJsonObject response, MeteorDDP * ddp ) {
        Q_UNUSED( ddp );

        if ( response.contains( "error" ) ) {
            QJsonObject errorObject = response["error"].toObject();

            if ( errorObject.contains( "message" ) ) {
                QString message = errorObject["message"].toString();
                emit error( message );
            }
        }
    };

    QSharedPointer<DDPUfsCreateRequest> request(
        new DDPUfsCreateRequest( mChannelId, mUri.fileName(), mDump.size(), mType.name(), success )
    );
    request->setError( errorFunc );

    if ( !mErrorStatus ) {
        mServer->sendDdprequest( request, true );
    }
}

void FileUploader::cancel()
{
    QMutexLocker locker( &mLock );
    mCanceled = true;

    if ( mPartialRequests.empty() ) {
        mCleanUp();
    }
}

void FileUploader::onUfsCreated( const QJsonObject &pResponse )
{
    if ( pResponse.contains( "result" ) ) {
        QJsonObject result = pResponse["result"].toObject();

        if ( result.contains( "url" ) && result.contains( "token" ) && result.contains( "fileId" ) ) {
            QString url = result["url"].toString();
            mToken = result["token"].toString();
            qDebug() << result.keys();
            qDebug() << result["fileId"].toString();
            QString fileId = result["fileId"].toString();
            this->mFileId = fileId;
            int pos = 0;
            int arrsize = mSize;
            int sizeInArray = 100000;
            emit ufsCreated( fileId );
            int chunkNum = arrsize / sizeInArray;
            chunkNum += ( arrsize % sizeInArray ) ? 1 : 0;
            qDebug() << "creating partials: " << chunkNum << "of bytes:" << mSize;

            if ( mSize == 0 ) {
                throw std::logic_error( "Zero byte file!" );
            }

            bool cancel = mCanceled;
            RestRequestCallback nextOne = [ = ]( QNetworkReply *, QJsonObject data, RestApi * ) {
                Q_UNUSED( data );
                QMutexLocker locker( &mLock );

                if ( cancel ) {
                    mPartialRequests.clear();
                    mCleanUp();
                } else {
                    nextRequest();
                }
            };

            for ( int i = 0; i < chunkNum; i++ ) {
                qDebug() << "creating partials nr " << chunkNum;
                QByteArray chunk = mDump.mid( pos, sizeInArray );
                double progress = ( 1.0 / chunkNum ) * ( i + 1 );
                QString completePath =  url + "&progress=" + QString::number( progress );
                auto partial = QSharedPointer<RestFileUploadRequest>::create( completePath, mToken, chunk );
                partial->setProgress( progress );
                qDebug() << "creating partial " << chunkNum << ":" << progress << "size:" << chunk.size();;
                partial->setSuccess( nextOne );
                mPartialRequests.append( partial );
                pos += chunk.size();
            }

            auto lastRequest = mPartialRequests.last();
            lastRequest->setSuccess( [ = ]( QNetworkReply * reply, QJsonObject data, RestApi * ) {
                Q_UNUSED( reply );
                onUploadCompleted( data );
            } );
            auto request = mPartialRequests.first();
            mServer->sendApiRequest( request, true );
        }
    }
}

void FileUploader::onUploadCompleted( const QJsonObject &pResponse )
{
    Q_UNUSED( pResponse );
    qDebug() << "upload completed";
    QString store = QStringLiteral( "rocketchat_uploads" );
    int major = std::get<0>( RocketChatServerConfig::serverVersion );
    int minor = std::get<1>( RocketChatServerConfig::serverVersion );
    int patch = std::get<2>( RocketChatServerConfig::serverVersion );

    if ( major != -1 && minor != -1 && patch != -1 ) {
        if ( major >= 0 && minor > 60 ) {
            store = QStringLiteral( "Uploads" );
        }
    }

    QJsonArray params = {mFileId, store, mToken};
    DdpCallback success2 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) {
        Q_UNUSED( pDdp );
        qDebug() << "ufs created response" << pResponse;

        if ( pResponse.contains( "result" ) ) {
            QJsonObject result = pResponse["result"].toObject();
            QJsonArray params2 = {mChannelId, QJsonValue::Null, result};
            DdpCallback success3 = [ = ]( QJsonObject pResponse, MeteorDDP * pDdp ) {
                Q_UNUSED( pResponse );
                Q_UNUSED( pDdp );
                qDebug() << "send file message response";
                mCleanUp();
            };
            auto request3 = QSharedPointer<DDPMethodRequest>::create( "sendFileMessage", params2, success3 );
            mServer->sendDdprequest( request3, true );
        }
    };
    auto request = QSharedPointer<DDPMethodRequest>::create( QStringLiteral( "ufsComplete" ), params, success2 );
    mServer->sendDdprequest( request, true );
    qDebug() << "file upload ready";
}

void FileUploader::nextRequest()
{

    if ( !mPartialRequests.isEmpty() ) {
        mPartialRequests.pop_front();
        auto request = mPartialRequests.first();
        double diff = request->getProgress() - progress;
        uint now = QDateTime::currentDateTime().toTime_t();
        if( diff >= 0.05 && now - lastUpdate){
            progress = request->getProgress();
            qDebug() << "progress is "<< progress;
            emit progressChanged( progress );
            lastUpdate = now;
        }
        mServer->sendApiRequest( request, true );
    }
}

QString FileUploader::getFileId() const
{
    return mFileId;
}