/*
Copyright (C) 2020-2021 Slava Monich at al.
This file is part of Fernschreiber.
Fernschreiber 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.
Fernschreiber 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 Fernschreiber. If not, see .
*/
#include "tdlibfile.h"
#define DEBUG_MODULE TDLibFile
#include "debuglog.h"
namespace {
const QString ID("id");
const QString EXPECTED_SIZE("expected_size");
const QString SIZE("size");
const QString LOCAL("local");
const QString CAN_BE_DELETED("can_be_deleted");
const QString CAN_BE_DOWNLOADED("can_be_downloaded");
const QString DOWNLOAD_OFFSET("download_offset");
const QString DOWNLOADED_PREFIX_SIZE("downloaded_prefix_size");
const QString DOWNLOADED_SIZE("downloaded_size");
const QString IS_DOWNLODING_ACTIVE("is_downloading_active");
const QString IS_DOWNLODING_COMPELETED("is_downloading_completed");
const QString PATH("path");
const QString REMOTE("remote");
const QString IS_UPLOADING_ACTIVE("is_uploading_active");
const QString IS_UPLOADING_COMPLETED("is_uploading_completed");
const QString UNIQUE_ID("unique_id");
const QString UPLOADED_SIZE("uploaded_size");
const QString _TYPE("@type");
const QString TYPE_FILE("file");
const QString TYPE_LOCAL_FILE("localFile");
const QString TYPE_REMOTE_FILE("remoteFile");
}
// s(SignalName,signalName)
#define QUEUED_SIGNALS(s) \
s(TdLib,tdlib) \
s(FileInfo,fileInfo) \
s(AutoLoad,autoLoad) \
s(Id,id) \
s(ExpectedSize,expectedSize) \
s(Size,size) \
s(Path,path) \
s(DownloadOffset,downloadOffset) \
s(DownloadedPrefixSize,downloadedPrefixSize) \
s(DownloadedSize,downloadedSize) \
s(CanBeDeleted,canBeDeleted) \
s(CanBeDownloaded,canBeDownloaded) \
s(DownloadingActive,downloadingActive) \
s(DownloadingCompleted,downloadingCompleted) \
s(RemoteId,remoteId) \
s(UniqueId,uniqueId) \
s(UploadedSize,uploadedSize) \
s(UploadingActive,uploadingActive) \
s(UploadingCompleted,uploadingCompleted)
typedef void (TDLibFile::*TDLibFileSignalEmitter)();
enum TDLibFileSignal {
#define SIGNAL_ENUM_(Name,name) Signal##Name##Changed,
QUEUED_SIGNALS(SIGNAL_ENUM_)
#undef SIGNAL_ENUM_
SignalCount
};
TDLibFile::TDLibFile()
{
init();
}
TDLibFile::TDLibFile(TDLibWrapper *tdlib)
{
init();
updateTDLibWrapper(tdlib);
// Reset queued signals
firstQueuedSignal = SignalCount;
queuedSignals = 0;
}
TDLibFile::TDLibFile(TDLibWrapper *tdlib, const QVariantMap &fileInfo)
{
init();
updateTDLibWrapper(tdlib);
updateFileInfo(fileInfo);
// Reset queued signals
firstQueuedSignal = SignalCount;
queuedSignals = 0;
}
void TDLibFile::init()
{
tdLibWrapper = Q_NULLPTR;
queuedSignals = 0;
firstQueuedSignal = SignalCount;
autoLoad = false;
downloadHoldOffTimer = 0;
id = 0;
expected_size = 0;
size = 0;
download_offset = 0;
downloaded_prefix_size = 0;
downloaded_size = 0;
can_be_deleted = false;
can_be_downloaded = false;
is_downloading_active = false;
is_downloading_completed = false;
uploaded_size = 0;
is_uploading_active = false;
is_uploading_completed = false;
}
void TDLibFile::queueSignal(uint signal)
{
const uint signalBit = 1 << signal;
if (queuedSignals) {
queuedSignals |= signalBit;
if (firstQueuedSignal > signal) {
firstQueuedSignal = signal;
}
} else {
queuedSignals = signalBit;
firstQueuedSignal = signal;
}
}
void TDLibFile::emitQueuedSignals()
{
static const TDLibFileSignalEmitter emitSignal[] = {
#define SIGNAL_EMITTER_(Name,name) &TDLibFile::name##Changed,
QUEUED_SIGNALS(SIGNAL_EMITTER_)
#undef SIGNAL_EMITTER_
};
if (queuedSignals) {
// Reset first queued signal before emitting the signals.
// Signal handlers may emit new signals.
uint i = firstQueuedSignal;
firstQueuedSignal = SignalCount;
for (; i < SignalCount && queuedSignals; i++) {
const uint signalBit = 1 << i;
if (queuedSignals & signalBit) {
queuedSignals &= ~signalBit;
emit (this->*(emitSignal[i]))();
}
}
}
}
void TDLibFile::setTDLibWrapper(QObject* obj)
{
updateTDLibWrapper(qobject_cast(obj));
emitQueuedSignals();
}
void TDLibFile::updateTDLibWrapper(TDLibWrapper* tdlib)
{
if (tdlib != tdLibWrapper) {
if (tdLibWrapper) {
tdLibWrapper->disconnect(this);
}
tdLibWrapper = tdlib;
if (tdlib) {
connect(tdlib, SIGNAL(fileUpdated(int,QVariantMap)), SLOT(handleFileUpdated(int,QVariantMap)));
if (autoLoad) {
downloadFile();
}
}
queueSignal(SignalTdLibChanged);
}
}
void TDLibFile::setAutoLoad(bool enableAutoLoad)
{
if (autoLoad != enableAutoLoad) {
autoLoad = enableAutoLoad;
queueSignal(SignalAutoLoadChanged);
if (autoLoad) {
downloadFile();
}
emitQueuedSignals();
}
}
bool TDLibFile::cancel()
{
if (id && tdLibWrapper && is_downloading_active) {
tdLibWrapper->cancelDownloadFile(id);
tdLibWrapper->deleteFile(id);
return true;
}
return false;
}
bool TDLibFile::load()
{
// Manual load ignores hold-off timer
if (downloadHoldOffTimer) {
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
}
return downloadFile();
}
bool TDLibFile::downloadFile()
{
if (id && tdLibWrapper && !downloadHoldOffTimer &&
!is_downloading_active && !is_downloading_completed && can_be_downloaded) {
downloadHoldOffTimer = startTimer(DownloadHoldOffMs);
tdLibWrapper->downloadFile(id);
return true;
}
return false;
}
void TDLibFile::setFileInfo(const QVariantMap &fileInfo)
{
updateFileInfo(fileInfo);
if (is_downloading_completed && downloadHoldOffTimer) {
// Don't need this timer anymore
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
}
if (autoLoad) {
downloadFile();
}
emitQueuedSignals();
}
void TDLibFile::timerEvent(QTimerEvent *)
{
killTimer(downloadHoldOffTimer);
downloadHoldOffTimer = 0;
if (autoLoad) {
downloadFile();
}
}
void TDLibFile::handleFileUpdated(int fileId, const QVariantMap &fileInfo)
{
if (id == fileId) {
LOG("File" << fileId << "updated");
setFileInfo(fileInfo);
emitQueuedSignals();
}
}
/*
{
"@type": "file",
"expected_size": 0,
"id": 12,
"size": 0,
"local": {
"@type": "localFile",
"can_be_deleted": false,
"can_be_downloaded": true,
"download_offset": 0,
"downloaded_prefix_size": 0,
"downloaded_size": 0,
"is_downloading_active": true,
"is_downloading_completed": false,
"path": ""
},
"remote": {
"@type": "remoteFile",
"id": "AQADAgATXYBrly4AAwIAA2E2itkW____9UdPDiSysLQ4fwIAARgE",
"is_uploading_active": false,
"is_uploading_completed": true,
"unique_id": "AQADXYBrly4AAzh_AgAB",
"uploaded_size": 0
}
}
*/
void TDLibFile::updateFileInfo(const QVariantMap &file)
{
if (file.value(_TYPE).toString() == TYPE_FILE) {
QString s;
qlonglong ll;
bool b, fileChanged = false;
int i = file.value(ID).toInt();
if (id != i) {
LOG("File id has changed" << id << "=>" << i);
id = i;
fileChanged = true;
queueSignal(SignalIdChanged);
}
ll = file.value(EXPECTED_SIZE).toLongLong();
if (expected_size != ll) {
expected_size = ll;
fileChanged = true;
queueSignal(SignalExpectedSizeChanged);
}
ll = file.value(SIZE).toLongLong();
if (size != ll) {
size = ll;
fileChanged = true;
queueSignal(SignalSizeChanged);
}
const QVariantMap local(file.value(LOCAL).toMap());
if (local.value(_TYPE).toString() == TYPE_LOCAL_FILE) {
ll = local.value(DOWNLOAD_OFFSET).toLongLong();
if (download_offset != ll) {
download_offset = ll;
fileChanged = true;
queueSignal(SignalDownloadOffsetChanged);
}
ll = local.value(DOWNLOADED_PREFIX_SIZE).toLongLong();
if (downloaded_prefix_size != ll) {
downloaded_prefix_size = ll;
fileChanged = true;
queueSignal(SignalDownloadedPrefixSizeChanged);
}
ll = local.value(DOWNLOADED_SIZE).toLongLong();
if (downloaded_size != ll) {
downloaded_size = ll;
fileChanged = true;
queueSignal(SignalDownloadedSizeChanged);
}
b = local.value(CAN_BE_DELETED).toBool();
if (can_be_deleted != b) {
can_be_deleted = b;
fileChanged = true;
queueSignal(SignalCanBeDeletedChanged);
}
b = local.value(CAN_BE_DOWNLOADED).toBool();
if (can_be_downloaded != b) {
can_be_downloaded = b;
fileChanged = true;
queueSignal(SignalCanBeDownloadedChanged);
}
b = local.value(IS_DOWNLODING_ACTIVE).toBool();
if (is_downloading_active != b) {
is_downloading_active = b;
fileChanged = true;
queueSignal(SignalDownloadingActiveChanged);
}
b = local.value(IS_DOWNLODING_COMPELETED).toBool();
if (is_downloading_completed != b) {
is_downloading_completed = b;
fileChanged = true;
queueSignal(SignalDownloadingCompletedChanged);
}
s = local.value(PATH).toString();
if (path != s) {
path = s;
fileChanged = true;
queueSignal(SignalPathChanged);
}
}
const QVariantMap remote(file.value(REMOTE).toMap());
if (remote.value(_TYPE).toString() == TYPE_REMOTE_FILE) {
ll = file.value(UPLOADED_SIZE).toLongLong();
if (uploaded_size != ll) {
uploaded_size = ll;
fileChanged = true;
queueSignal(SignalUploadedSizeChanged);
}
b = file.value(IS_UPLOADING_ACTIVE).toBool();
if (is_uploading_active != b) {
is_uploading_active = b;
fileChanged = true;
queueSignal(SignalUploadingActiveChanged);
}
b = file.value(IS_UPLOADING_COMPLETED).toBool();
if (is_uploading_completed != b) {
is_uploading_completed = b;
fileChanged = true;
queueSignal(SignalUploadingCompletedChanged);
}
s = local.value(ID).toString();
if (remote_id != s) {
remote_id = s;
fileChanged = true;
queueSignal(SignalRemoteIdChanged);
}
s = local.value(UNIQUE_ID).toString();
if (unique_id != s) {
unique_id = s;
fileChanged = true;
queueSignal(SignalUniqueIdChanged);
}
}
if (fileChanged) {
infoMap = file;
queueSignal(SignalFileInfoChanged);
}
}
}