#include "InfoDialog.h"

#include "AccountDetailsDialog.h"
#include "AccountDetailsManager.h"
#include "AppState.h"
#include "CreateRemoveBackupsManager.h"
#include "CreateRemoveSyncsManager.h"
#include "DialogOpener.h"
#include "MegaApplication.h"
#include "offer/OfferComponent.h"
#include "Platform.h"
#include "QmlDialogWrapper.h"
#include "ServiceUrls.h"
#include "StalledIssuesDialog.h"
#include "StalledIssuesModel.h"
#include "StatsEventHandler.h"
#include "ThemeManager.h"
#include "TokenParserWidgetManager.h"
#include "TransferManager.h"
#include "TransferQuota.h"
#include "ui_InfoDialog.h"
#include "UpsellController.h"
#include "UserMessageController.h"
#include "UserMessageDelegate.h"
#include "Utilities.h"

#include <QColor>
#include <QDesktopServices>
#include <QEvent>
#include <QFileInfo>
#include <QHelpEvent>
#include <QRect>
#include <QScrollBar>
#include <QSignalMapper>
#include <QTimer>
#include <QToolTip>
#include <QUrl>
#include <QVBoxLayout>

#include <cassert>
#include <chrono>

using namespace mega;
using namespace std::chrono;

static constexpr int DEFAULT_MIN_PERCENTAGE{1};
static constexpr int FONT_SIZE_BUSINESS_PX{12};
static constexpr int FONT_SIZE_NO_BUSINESS_PX{12};

static const char* TRANSPARENT_HEADER = "transparent_header";

void InfoDialog::pauseResumeClicked()
{
    app->pauseTransfers();
}

void InfoDialog::pauseResumeHovered(QMouseEvent *event)
{
    if (mPreferences->logged())
    {
        QString tooltip(mPreferences->getGlobalPaused() ?
                            TransferManager::getResumeAllTransfersTooltip() :
                            TransferManager::getPauseAllTransfersTooltip());
        QToolTip::showText(event->globalPos(), tooltip);
    }
}

void InfoDialog::generalAreaHovered(QMouseEvent *event)
{
    QToolTip::showText(event->globalPos(), tr("Open Transfer Manager"));
}
void InfoDialog::dlAreaHovered(QMouseEvent *event)
{
    QToolTip::showText(event->globalPos(), tr("Open Downloads"));
}

void InfoDialog::upAreaHovered(QMouseEvent *event)
{
    QToolTip::showText(event->globalPos(), tr("Open Uploads"));
}

InfoDialog::InfoDialog(MegaApplication* app, QWidget* parent, InfoDialog* olddialog):
    QDialog(parent),
    ui(new Ui::InfoDialog),
    mIndexing(false),
    mWaiting(false),
    mSyncing(false),
    mTransferring(false),
    mTransferManager(nullptr),
    mUpsellController(nullptr),
    mPreferences(Preferences::instance()),
    mSyncInfo(SyncInfo::instance()),
    qtBugFixer(this)
{
    ui->setupUi(this);
    connect(AppState::instance().get(),
            &AppState::appStateChanged,
            this,
            [this](AppState::AppStates oldAppState, AppState::AppStates newAppState)
            {
                // We want to show the infodialog only in NOMINAL mode.
                // Show Guest Dialog otherwise
                if (newAppState != AppState::NOMINAL)
                {
                    hide();
                    deleteLater();
                }
            });

    filterMenu = new FilterAlertWidget(this);
    connect(filterMenu,
            SIGNAL(filterClicked(MessageType)),
            this,
            SLOT(applyFilterOption(MessageType)));

    setUnseenNotifications(0);

    connect(ui->bTransferManager, SIGNAL(pauseResumeClicked()), this, SLOT(pauseResumeClicked()));
    connect(ui->bTransferManager,
            &TransfersSummaryQuickWidget::transferManagerClicked,
            this,
            &InfoDialog::onTransferManagerClicked);

    connect(ui->wSortNotifications, SIGNAL(clicked()), this, SLOT(onActualFilterClicked()));

    connect(app->getTransfersModel(),
            &TransfersModel::transfersCountUpdated,
            this,
            &InfoDialog::updateTransfersCount);
    connect(app->getTransfersModel(),
            &TransfersModel::transfersProcessChanged,
            this,
            &InfoDialog::onTransfersStateChanged);

    connect(ui->wListTransfers,
            &InfoDialogTransfersWidget::topTransferTypeChanged,
            this,
            &InfoDialog::onTopTransferTypeChanged);

    connect(mPreferences.get(),
            &Preferences::valueChanged,
            this,
            [this](const QString& key)
            {
                if (key == Preferences::wasPausedKey)
                {
                    ui->bTransferManager->setPaused(mPreferences->getGlobalPaused());
                }
            });

    connect(ui->bCreateSync, &QAbstractButton::clicked, this, &InfoDialog::onAddSyncClicked);

    connect(ui->bAvatar,
            &AvatarWidget::clicked,
            this,
            []()
            {
                MegaSyncApp->openSettings(SettingsDialog::ACCOUNT_TAB);
                MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
                    AppStatsEvents::EventType::AVATAR_CLICKED,
                    true);
            });

    // Set window properties
#ifdef Q_OS_LINUX
    doNotActAsPopup = Platform::getInstance()->getValue("USE_MEGASYNC_AS_REGULAR_WINDOW", false);

    if (!doNotActAsPopup && QSystemTrayIcon::isSystemTrayAvailable())
    {
        // To avoid issues with text input we implement a popup ourselves
        // instead of using Qt::Popup by listening to the WindowDeactivate
        // event.
        Qt::WindowFlags flags = Qt::FramelessWindowHint;

        if (Platform::getInstance()->isTilingWindowManager())
        {
            flags |= Qt::Dialog;
        }

        setWindowFlags(flags);
    }
    else
    {
        setWindowFlags(Qt::Window);
        doNotActAsPopup = true; //the first time systray is not available will set this flag to true to disallow popup until restarting
    }
#elif defined(_WIN32)
    setWindowFlags(Qt::FramelessWindowHint | Qt::Popup | Qt::NoDropShadowWindowHint);
#else // OS X
    setWindowFlags(Qt::FramelessWindowHint);
#endif

#ifdef _WIN32
    if (qEnvironmentVariableIsSet("QT_SCREEN_SCALE_FACTORS") ||
        qEnvironmentVariableIsSet("QT_SCALE_FACTOR"))
    {
        //do not use WA_TranslucentBackground when using custom scale factors in windows
        setStyleSheet(styleSheet().append(QString::fromUtf8("#wInfoDialogIn{border-radius: 0px;}" ) ));
    }
    else
#endif
    {
        setAttribute(Qt::WA_TranslucentBackground);
    }

    //Initialize fields
    this->app = app;

    circlesShowAllActiveTransfersProgress = true;

    actualAccountType = -1;

    notificationsReady = false;
    ui->sNotifications->setCurrentWidget(ui->pNoNotifications);

    overQuotaState = false;
    storageState = Preferences::STATE_BELOW_OVER_STORAGE;

    reset();

    hideSomeIssues();

    initNotificationArea();

    //Initialize header dialog and disable chat features
    ui->wHeader->setStyleSheet(QString::fromUtf8("#wHeader {border: none;}"));

    //Set properties of some widgets
    ui->sActiveTransfers->setCurrentWidget(ui->pUpdated);

    ui->sStorage->setCurrentWidget(ui->wCircularStorage);
    ui->sQuota->setCurrentWidget(ui->wCircularQuota);

#if defined(__APPLE__) || defined(Q_OS_LINUX)
    installEventFilter(this);
#endif

    ui->wStorageUsage->installEventFilter(this);

    ui->lOQDesc->setTextFormat(Qt::RichText);

    mState = StatusInfo::TRANSFERS_STATES::STATE_STARTING;
    ui->wStatus->setState(mState);
    megaApi = app->getMegaApi();

    actualAccountType = -1;

    connect(mSyncInfo, SIGNAL(syncDisabledListUpdated()), this, SLOT(updateDialogState()));

    connect(ui->wPSA, SIGNAL(PSAseen(int)), app, SLOT(PSAseen(int)), Qt::QueuedConnection);

    connect(ui->sTabs, SIGNAL(currentChanged(int)), this, SLOT(sTabsChanged(int)), Qt::QueuedConnection);

    on_tTransfers_clicked();

    ui->wListTransfers->setupTransfers();

    //Create the overlay widget with a transparent background
    overlay = new OverlayWidget(ui->pUpdated);
    overlay->resize(ui->pUpdated->size());
    overlay->setCursor(Qt::PointingHandCursor);
    overlay->raise();
    overlay->show();

    connect(overlay, &OverlayWidget::clicked, this, &InfoDialog::onOverlayClicked);
    connect(this, SIGNAL(openTransferManager(int)), app, SLOT(externalOpenTransferManager(int)));

    if (mPreferences->logged())
    {
        setAvatar();
        setUsage();
        createUpsellController();
    }
    highDpiResize.init(this);

#ifdef _WIN32
    lastWindowHideTime = std::chrono::steady_clock::now() - 5s;

    PSA_info *psaData = olddialog ? olddialog->getPSAdata() : nullptr;
    if (psaData)
    {
        this->setPSAannouncement(psaData->idPSA, psaData->title, psaData->desc,
                                 psaData->urlImage, psaData->textButton, psaData->urlClick);
        delete psaData;
    }
#endif

    adjustSize();

    // By default, the header has a background-color
    ui->wHeader->setProperty(TRANSPARENT_HEADER, false);

    mTransferScanCancelUi = new TransferScanCancelUi(ui->sTabs, ui->pTransfersTab);
    connect(mTransferScanCancelUi, &TransferScanCancelUi::cancelTransfers,
            this, &InfoDialog::cancelScanning);
    connect(mTransferScanCancelUi,
            &TransferScanCancelUi::visibilityChanged,
            this,
            &InfoDialog::onScanningVisibilityChanged);

    connect(MegaSyncApp->getStalledIssuesModel(), &StalledIssuesModel::stalledIssuesChanged,
            this,  &InfoDialog::onStalledIssuesChanged);
    onStalledIssuesChanged();

    connect(AccountDetailsManager::instance(),
            &AccountDetailsManager::accountDetailsUpdated,
            this,
            &InfoDialog::updateUsageAndAccountType);

    updateUpgradeButtonText();
    updateCreateSyncButtonText();

    ui->issuesBanner->setType(BannerWidget::Type::BANNER_ERROR);
    ui->issuesBanner->setTitle(
        QCoreApplication::translate("SomeIssuesOccurredMessage", "Some issues ocurred."));
    ui->issuesBanner->setLinkText(
        QCoreApplication::translate("SomeIssuesOccurredMessage", "View..."));

    connect(ui->issuesBanner,
            &BannerWidget::linkActivated,
            this,
            &InfoDialog::showStalledIssuesDialog);
}

InfoDialog::~InfoDialog()
{
    removeEventFilter(this);
    if(ui->tvNotifications->itemDelegate())
    {
        // Remove delegate cache before deleting the parent QTreeView widget
        delete ui->tvNotifications->itemDelegate();
    }
    delete ui;
    delete filterMenu;
}

PSA_info *InfoDialog::getPSAdata()
{
    if (ui->wPSA->isPSAshown())
    {
        PSA_info* info = new PSA_info(ui->wPSA->getPSAdata());
        return info;
    }

    return nullptr;
}

void InfoDialog::showEvent(QShowEvent *event)
{
    emit ui->sTabs->currentChanged(ui->sTabs->currentIndex());
    isShown = true;

    app->getNotificationController()->requestNotifications();

    repositionInfoDialog();

    QDialog::showEvent(event);
}

void InfoDialog::moveEvent(QMoveEvent*)
{
    qtBugFixer.onEndMove();
}

void InfoDialog::setBandwidthOverquotaState(QuotaState state)
{
    transferQuotaState = state;
    setUsage();
}

void InfoDialog::updateUsageAndAccountType()
{
    setUsage();
    setAccountType(mPreferences->accountType());
    updateUpgradeButtonState();
}

void InfoDialog::enableTransferOverquotaAlert()
{
    if (!transferOverquotaAlertEnabled)
    {
        transferOverquotaAlertEnabled = true;
        emit transferOverquotaMsgVisibilityChange(transferOverquotaAlertEnabled);
    }
    updateDialogState();
}

void InfoDialog::enableTransferAlmostOverquotaAlert()
{
    if (!transferAlmostOverquotaAlertEnabled)
    {
        transferAlmostOverquotaAlertEnabled = true;
        emit almostTransferOverquotaMsgVisibilityChange(transferAlmostOverquotaAlertEnabled);
    }
    updateDialogState();
}

void InfoDialog::hideEvent(QHideEvent *event)
{
    if (filterMenu && filterMenu->isVisible())
    {
        filterMenu->hide();
    }

    QTimer::singleShot(1000, this, [this] () {
        if (!isShown)
        {
            emit ui->sTabs->currentChanged(-1);
        }
    });

    isShown = false;
    QDialog::hideEvent(event);

#ifdef _WIN32
    lastWindowHideTime = std::chrono::steady_clock::now();
#endif

}

void InfoDialog::setAvatar()
{
    ui->bAvatar->setUserEmail(mPreferences->email().toUtf8().constData());
}

void InfoDialog::createUpsellController()
{
    if (!mUpsellController)
    {
        mUpsellController = std::make_unique<UpsellController>(true, nullptr);
        mUpsellController->requestPricingData();
    }
}

void InfoDialog::setUsage()
{
    auto accType = mPreferences->accountType();

    QString quotaStringFormat = QString::fromLatin1("<span style='color:%1; font-size:%2px;'>%3</span>");
    QString quotaStringFormatNonColored =
        QString::fromLatin1("<span style='font-size:%1px;'>%2</span>");
    // Get font to adapt size to widget if needed
    // Getting font from lUsedStorage considering both
    // lUsedStorage and lUsedTransfer use the same font.
    ui->lUsedStorage->style()->polish(ui->lUsedStorage);
    QFont font (ui->lUsedStorage->font());

    // ---------- Process storage usage

    // Get useful data
    auto totalStorage(mPreferences->totalStorage());
    auto usedStorage(mPreferences->usedStorage());

    QString usageColorS;
    QString usedStorageString = Utilities::getSizeString(usedStorage);
    QString totalStorageString;
    QString storageUsageStringFormatted (usedStorageString);
    auto textPrimaryColor =
        TokenParserWidgetManager::instance()->getColor(QLatin1String("text-primary"));
    auto textSecondaryColor =
        TokenParserWidgetManager::instance()->getColor(QLatin1String("text-secondary"));
    if (Utilities::isBusinessAccount())
    {
        ui->sStorage->setCurrentWidget(ui->wBusinessStorage);
        ui->wCircularStorage->setState(CircularUsageProgressBar::STATE_OK);
        usageColorS = textSecondaryColor.name(QColor::HexArgb);
        font.setPixelSize(FONT_SIZE_BUSINESS_PX);
    }
    else
    {
        int parts = 0;

        if (totalStorage != 0ULL)
        {
            switch (mPreferences->getStorageState())
            {
                case MegaApi::STORAGE_STATE_PAYWALL:
                // Fallthrough
                case MegaApi::STORAGE_STATE_RED:
                {
                    ui->wCircularStorage->setState(CircularUsageProgressBar::STATE_OVER);
                    usageColorS = TokenParserWidgetManager::instance()
                                      ->getColor(QLatin1String("text-error"))
                                      .name(QColor::HexArgb);
                    break;
                }
                case MegaApi::STORAGE_STATE_ORANGE:
                {
                    ui->wCircularStorage->setState(CircularUsageProgressBar::STATE_WARNING);
                    usageColorS = TokenParserWidgetManager::instance()
                                      ->getColor(QLatin1String("text-warning"))
                                      .name(QColor::HexArgb);
                    break;
                }
                case MegaApi::STORAGE_STATE_UNKNOWN:
                // Fallthrough
                case MegaApi::STORAGE_STATE_GREEN:
                // Fallthrough
                default:
                {
                    ui->wCircularStorage->setState(CircularUsageProgressBar::STATE_OK);
                    usageColorS = textPrimaryColor.name(QColor::HexArgb);
                    break;
                }
            }

            parts = usedStorage ?
                        std::max(Utilities::partPer(usedStorage, totalStorage),
                                 DEFAULT_MIN_PERCENTAGE)
                                : 0;

            totalStorageString = Utilities::getSizeString(totalStorage);

            storageUsageStringFormatted = Utilities::getTranslatedSeparatorTemplate().arg(
                usedStorageString,
                totalStorageString);
        }

        ui->wCircularStorage->setValue(parts);
        ui->sStorage->setCurrentWidget(ui->wCircularStorage);
        font.setPixelSize(FONT_SIZE_NO_BUSINESS_PX);
    }


    // ---------- Process transfer usage

    // Get useful data
    auto totalTransfer(mPreferences->totalBandwidth());
    auto usedTransfer(mPreferences->usedBandwidth());

    QString usageColorT;
    QString usedTransferString(Utilities::getSizeString(usedTransfer));
    QString totalTransferString;
    QString transferUsageStringFormatted(usedTransferString);

    if (Utilities::isBusinessAccount())
    {
        ui->sQuota->setCurrentWidget(ui->wBusinessQuota);
        usageColorT = textSecondaryColor.name(QColor::HexArgb);
        ui->wCircularStorage->setTotalValueUnknown();
    }
    else
    {
        // Set color according to state
        switch (transferQuotaState)
        {
            case QuotaState::OK:
            {
                ui->wCircularQuota->setState(CircularUsageProgressBar::STATE_OK);
                usageColorT = textPrimaryColor.name(QColor::HexArgb);

                break;
            }
            case QuotaState::WARNING:
            {
                ui->wCircularQuota->setState(CircularUsageProgressBar::STATE_WARNING);
                usageColorS = TokenParserWidgetManager::instance()
                                  ->getColor(QLatin1String("text-warning"))
                                  .name(QColor::HexArgb);
                break;
            }
            case QuotaState::OVERQUOTA:
            // Fallthrough
            case QuotaState::FULL:
            {
                ui->wCircularQuota->setState(CircularUsageProgressBar::STATE_OVER);
                usageColorS = TokenParserWidgetManager::instance()
                                  ->getColor(QLatin1String("text-error"))
                                  .name(QColor::HexArgb);
                break;
            }
            default:
            {
                MegaApi::log(MegaApi::LOG_LEVEL_ERROR, "Unknown transfer quota state");
                break;
            }
        }

        if (accType == Preferences::ACCOUNT_TYPE_FREE)
        {
            ui->wCircularQuota->setTotalValueUnknown(transferQuotaState != QuotaState::FULL
                                                        && transferQuotaState != QuotaState::OVERQUOTA);
        }
        else
        {
            int parts = 0;

            if (totalTransfer == 0ULL)
            {
                ui->wCircularQuota->setTotalValueUnknown();
            }
            else
            {
                parts = usedTransfer ?
                            std::max(Utilities::partPer(usedTransfer, totalTransfer),
                                     DEFAULT_MIN_PERCENTAGE)
                                     : 0;

                totalTransferString = Utilities::getSizeString(totalTransfer);

                transferUsageStringFormatted = Utilities::getTranslatedSeparatorTemplate().arg(
                    usedTransferString,
                    totalTransferString);
            }

            ui->wCircularQuota->setValue(parts);
        }

        ui->sQuota->setCurrentWidget(ui->wCircularQuota);
    }

    // Now compute the font size and set usage strings
    // Find correct font size so that the string does not overflow

    auto contentsMargins = ui->lUsedStorage->contentsMargins();
    auto margin = contentsMargins.left() + contentsMargins.right() + 2 * ui->lUsedStorage->margin();
    auto storageStringMaxWidth = ui->wStorageDetails->contentsRect().width() - margin;

    contentsMargins = ui->lUsedQuota->contentsMargins();
    margin = contentsMargins.left() + contentsMargins.right() + 2 * ui->lUsedQuota->margin();
    auto transferStringMaxWidth = ui->wQuotaDetails->contentsRect().width() - margin;

    QFontMetrics fMetrics (font);
    while ((fMetrics.horizontalAdvance(storageUsageStringFormatted) >= storageStringMaxWidth
            || fMetrics.horizontalAdvance(transferUsageStringFormatted) >= transferStringMaxWidth)
           && font.pixelSize() > 1)
    {
        font.setPixelSize(font.pixelSize() - 1);
        fMetrics = QFontMetrics(font);
    }

    // Now apply format (color, font size) to Storage usage string
    auto usedStorageStringFormatted = quotaStringFormat.arg(usageColorS,
                                                            QString::number(font.pixelSize()),
                                                            usedStorageString);
    if (totalStorage == 0ULL || Utilities::isBusinessAccount())
    {
        storageUsageStringFormatted = usedStorageStringFormatted;
    }
    else
    {
        storageUsageStringFormatted = Utilities::getTranslatedSeparatorTemplate().arg(
            usedStorageStringFormatted,
            totalStorageString);
        storageUsageStringFormatted =
            quotaStringFormatNonColored.arg(QString::number(font.pixelSize()),
                                            storageUsageStringFormatted);
    }

    ui->lUsedStorage->setText(storageUsageStringFormatted);

    // Now apply format (color, font size) to Transfer usage string
    auto usedTransferStringFormatted = quotaStringFormat.arg(usageColorT,
                                                             QString::number(font.pixelSize()),
                                                             usedTransferString);
    if (totalTransfer == 0ULL || Utilities::isBusinessAccount())
    {
        transferUsageStringFormatted = usedTransferStringFormatted;
    }
    else
    {
        transferUsageStringFormatted = Utilities::getTranslatedSeparatorTemplate().arg(
            usedTransferStringFormatted,
            totalTransferString);
        transferUsageStringFormatted =
            quotaStringFormatNonColored.arg(QString::number(font.pixelSize()),
                                            transferUsageStringFormatted);
    }

    ui->lUsedQuota->setText(transferUsageStringFormatted);
}

void InfoDialog::updateTransfersCount()
{
    if (auto transferModel = app->getTransfersModel())
    {
        auto transfersCountUpdated = transferModel->getLastTransfersCount();
        int ongoingTransfers =
            (transfersCountUpdated.totalDownloads != transfersCountUpdated.completedDownloads()) +
            (transfersCountUpdated.totalUploads != transfersCountUpdated.completedUploads());
        auto totalTransfers =
            transfersCountUpdated.totalDownloads + transfersCountUpdated.totalUploads;
        auto completedTransfers =
            transfersCountUpdated.completedDownloads() + transfersCountUpdated.failedDownloads +
            transfersCountUpdated.completedUploads() + transfersCountUpdated.failedUploads;

        ui->bTransferManager->setTransfersCount(completedTransfers, totalTransfers);
        ui->bTransferManager->setOngoingTransfers(ongoingTransfers);
    }
}

void InfoDialog::onTransfersStateChanged()
{
    if(app->getTransfersModel())
    {
        auto transfersCountUpdated = app->getTransfersModel()->getLastTransfersCount();

        if(transfersCountUpdated.pendingTransfers() == 0)
        {
            if (!overQuotaState && (ui->sActiveTransfers->currentWidget() != ui->pUpdated))
            {
                updateDialogState();
            }
        }

        ui->wStatus->update();
    }
}

void InfoDialog::onStalledIssuesChanged()
{
    if (!MegaSyncApp->getStalledIssuesModel()->isEmpty())
    {
        showSomeIssues();
    }
    else
    {
        hideSomeIssues();
    }

    updateState();
}

void InfoDialog::onScanningVisibilityChanged(bool state)
{
    if (state && ui->wPSA->isPSAshown())
    {
        changePSAVisibility(false);
    }
    else if (!state & !ui->wPSA->isPSAshown() && ui->wPSA->isActive())
    {
        changePSAVisibility(true);
    }
}

void InfoDialog::setIndexing(bool indexing)
{
    mIndexing = indexing;
}

void InfoDialog::setWaiting(bool waiting)
{
    mWaiting = waiting;
}

void InfoDialog::setSyncing(bool syncing)
{
    mSyncing = syncing;
}

void InfoDialog::setTransferring(bool transferring)
{
    mTransferring = transferring;
}

void InfoDialog::setOverQuotaMode(bool state)
{
    if (overQuotaState == state)
    {
        return;
    }

    overQuotaState = state;
    ui->wStatus->setOverQuotaState(state);
}

void InfoDialog::setAccountType(int accType)
{
    if (actualAccountType == accType)
    {
        return;
    }

    actualAccountType = accType;
}

void InfoDialog::updateBlockedState()
{
    if (!mPreferences->logged())
    {
        return;
    }
}

void InfoDialog::updateState()
{
    if (!mPreferences->logged())
    {
        return;
    }

    if(!checkFailedState())
    {
        if (mTransferScanCancelUi != nullptr && mTransferScanCancelUi->isActive())
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_INDEXING);
        }
        else if (mPreferences->getGlobalPaused())
        {
            mState = StatusInfo::TRANSFERS_STATES::STATE_PAUSED;
        }
        else if (mIndexing)
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_INDEXING);
        }
        else if (mSyncing)
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_SYNCING);
        }
        else if (mWaiting)
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_WAITING);
        }
        else if (mTransferring)
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_TRANSFERRING);
        }
        else
        {
            changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_UPDATED);
        }
    }

    updateHeaderBackground();

    if(ui->wStatus->getState() != mState)
    {
        ui->wStatus->setState(mState);
        if(mTransferManager)
        {
            mTransferManager->setTransferState(mState);
        }
    }
}

bool InfoDialog::checkFailedState()
{
    auto isFailed(false);

    if ((app->getTransfersModel() && app->getTransfersModel()->failedTransfers()) ||
        (app->getStalledIssuesModel() && !app->getStalledIssuesModel()->isEmpty()))
    {
        changeStatusState(StatusInfo::TRANSFERS_STATES::STATE_FAILED);
        isFailed = true;
    }

    return isFailed;
}

void InfoDialog::onAddSync(mega::MegaSync::SyncType type)
{
    switch (type)
    {
        case mega::MegaSync::TYPE_TWOWAY:
        {
            MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(AppStatsEvents::EventType::MENU_ADD_SYNC_CLICKED, true);
            addSync(SyncInfo::SyncOrigin::CONTEXT_MENU_ORIGIN);
            break;
        }
        case mega::MegaSync::TYPE_BACKUP:
        {
            MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(AppStatsEvents::EventType::MENU_ADD_BACKUP_CLICKED, true);
            addBackup(SyncInfo::SyncOrigin::CONTEXT_MENU_ORIGIN);
            break;
        }
        default:
        {
            break;
        }
    }
}

void InfoDialog::onAddBackup()
{
    onAddSync(mega::MegaSync::TYPE_BACKUP);
}

void InfoDialog::updateDialogState()
{
    updateState();
    const bool transferOverQuotaEnabled{
        (transferQuotaState == QuotaState::FULL || transferQuotaState == QuotaState::OVERQUOTA) &&
        transferOverquotaAlertEnabled};

    if (storageState == Preferences::STATE_PAYWALL)
    {
        if (mUpsellController)
        {
            MegaIntegerList* tsWarnings = megaApi->getOverquotaWarningsTs();
            const char* email = megaApi->getMyEmail();

            long long numFiles{mPreferences->cloudDriveFiles() + mPreferences->vaultFiles() +
                               mPreferences->rubbishFiles()};
            QString contactMessage =
                tr("We have contacted you by email to [A] on [B] but you still "
                   "have "
                   "%n file taking "
                   "up [D] in your MEGA account, which requires you to have [E].",
                   "",
                   static_cast<int>(numFiles));

            QString overDiskText =
                contactMessage.replace(QString::fromUtf8("[A]"), QString::fromUtf8(email))
                    .replace(QString::fromUtf8("[B]"),
                             Utilities::getReadableStringFromTs(tsWarnings))
                    .replace(QString::fromUtf8("[D]"),
                             Utilities::getSizeString(mPreferences->usedStorage()))
                    .replace(QString::fromUtf8("[E]"),
                             mUpsellController->getMinProPlanNeeded(mPreferences->usedStorage()));
            ui->lOverDiskQuotaLabel->setText(overDiskText);

            int64_t remainDaysOut(0);
            int64_t remainHoursOut(0);
            Utilities::getDaysAndHoursToTimestamp(megaApi->getOverquotaDeadlineTs(),
                                                  remainDaysOut,
                                                  remainHoursOut);
            if (remainDaysOut > 0)
            {
                QString descriptionDays = tr("You have %n day left to upgrade. After that, "
                                             "your data is subject to deletion.",
                                             "",
                                             static_cast<int>(remainDaysOut));
                ui->lWarningOverDiskQuota->setText(descriptionDays);
            }
            else if (remainDaysOut == 0 && remainHoursOut > 0)
            {
                QString descriptionHours = tr("You have %n hour left to upgrade. After "
                                              "that, your data is subject to deletion.",
                                              "",
                                              static_cast<int>(remainHoursOut));
                ui->lWarningOverDiskQuota->setText(descriptionHours);
            }
            else
            {
                ui->lWarningOverDiskQuota->setText(
                    tr("You must act immediately to save your data"));
            }

            delete tsWarnings;
            delete[] email;

            ui->sActiveTransfers->setCurrentWidget(ui->pOverDiskQuotaPaywall);
            overlay->setVisible(false);
            changePSAVisibility(false);
        }
    }
    else if (storageState == Preferences::STATE_OVER_STORAGE)
    {
        const bool transferIsOverQuota{transferQuotaState == QuotaState::FULL ||
                                       transferQuotaState == QuotaState::OVERQUOTA};
        const bool userIsFree{mPreferences->accountType() ==
                              Preferences::Preferences::ACCOUNT_TYPE_FREE};
        if (transferIsOverQuota && userIsFree)
        {
            ui->bOQIcon->setIcon(
                QIcon(QString::fromLatin1("://images/storage_transfer_full_FREE.png")));
            ui->bOQIcon->setIconSize(QSize(96, 96));
        }
        else if (transferIsOverQuota && !userIsFree)
        {
            ui->bOQIcon->setIcon(
                QIcon(QString::fromLatin1("://images/storage_transfer_full_PRO.png")));
            ui->bOQIcon->setIconSize(QSize(96, 96));
        }
        else
        {
            ui->bOQIcon->setIcon(QIcon(QString::fromLatin1("://images/storage_full.png")));
            ui->bOQIcon->setIconSize(QSize(64, 64));
        }
        ui->lOQTitle->setText(tr("Your MEGA account is full."));
        ui->lOQDesc->setText(tr("All file uploads are currently disabled.") +
                             QString::fromUtf8("<br>") + tr("Please upgrade to PRO."));
        ui->bBuyQuota->setText(tr("Buy more space"));
        ui->sActiveTransfers->setCurrentWidget(ui->pOverquota);
        overlay->setVisible(false);
        changePSAVisibility(false);
    }
    else if (transferOverQuotaEnabled)
    {
        ui->lOQTitle->setText(tr("Transfer quota exceeded"));

        if (mPreferences->accountType() == Preferences::ACCOUNT_TYPE_FREE)
        {
            ui->lOQDesc->setText(tr("Your queued transfers exceed the current quota "
                                    "available for your IP address."));
            ui->bBuyQuota->setText(tr("Upgrade Account"));
            ui->bDiscard->setText(tr("I will wait"));
        }
        else
        {
            ui->lOQDesc->setText(
                tr("You can't continue downloading as you don't have enough transfer "
                   "quota left on this account. "
                   "To continue downloading, purchase a new plan, or if you have a "
                   "recurring subscription with MEGA, "
                   "you can wait for your plan to renew."));
            ui->bBuyQuota->setText(tr("Buy new plan"));
            ui->bDiscard->setText(tr("Dismiss"));
        }
        ui->bOQIcon->setIcon(QIcon(QString::fromLatin1(":/images/transfer_empty_64.png")));
        ui->bOQIcon->setIconSize(QSize(64, 64));
        ui->sActiveTransfers->setCurrentWidget(ui->pOverquota);
        overlay->setVisible(false);
        changePSAVisibility(false);
    }
    else if (storageState == Preferences::STATE_ALMOST_OVER_STORAGE)
    {
        ui->bOQIcon->setIcon(QIcon(QString::fromLatin1("://images/storage_almost_full.png")));
        ui->bOQIcon->setIconSize(QSize(64, 64));
        ui->lOQTitle->setText(tr("You're running out of storage space."));
        ui->lOQDesc->setText(tr("Upgrade to PRO now before your account runs full "
                                "and your uploads to MEGA stop."));
        ui->bBuyQuota->setText(tr("Buy more space"));
        ui->sActiveTransfers->setCurrentWidget(ui->pOverquota);
        overlay->setVisible(false);
        changePSAVisibility(false);
    }
    else if (transferQuotaState == QuotaState::WARNING && transferAlmostOverquotaAlertEnabled)
    {
        ui->bOQIcon->setIcon(QIcon(QString::fromLatin1(":/images/transfer_empty_64.png")));
        ui->bOQIcon->setIconSize(QSize(64, 64));
        ui->lOQTitle->setText(tr("Limited available transfer quota"));
        ui->lOQDesc->setText(tr("Downloading may be interrupted as you have used 90% of your "
                                "transfer quota on this "
                                "account. To continue downloading, purchase a new plan, or if you "
                                "have a recurring "
                                "subscription with MEGA, you can wait for your plan to renew. "));
        ui->bBuyQuota->setText(tr("Buy new plan"));

        ui->sActiveTransfers->setCurrentWidget(ui->pOverquota);
        overlay->setVisible(false);
        changePSAVisibility(false);
    }
    else if (mSyncInfo->hasUnattendedDisabledSyncs(
                 {mega::MegaSync::TYPE_TWOWAY, mega::MegaSync::TYPE_BACKUP}))
    {
        if (mSyncInfo->hasUnattendedDisabledSyncs(mega::MegaSync::TYPE_TWOWAY) &&
            mSyncInfo->hasUnattendedDisabledSyncs(mega::MegaSync::TYPE_BACKUP))
        {
            ui->sActiveTransfers->setCurrentWidget(ui->pAllSyncsDisabled);
        }
        else if (mSyncInfo->hasUnattendedDisabledSyncs(mega::MegaSync::TYPE_BACKUP))
        {
            ui->sActiveTransfers->setCurrentWidget(ui->pBackupsDisabled);
        }
        else
        {
            ui->sActiveTransfers->setCurrentWidget(ui->pSyncsDisabled);
        }
        overlay->setVisible(false);
        changePSAVisibility(false);
    }
    else
    {
        if (app->getTransfersModel())
        {
            auto transfersCount = app->getTransfersModel()->getTransfersCount();

            if (ui->wPSA->isPSAready())
            {
                changePSAVisibility(true);
            }
            else
            {
                changePSAVisibility(false);
            }

            if (transfersCount.totalDownloads || transfersCount.totalUploads)
            {
                overlay->setVisible(false);
                ui->sActiveTransfers->setCurrentWidget(ui->pTransfers);
            }
            else
            {
                ui->sActiveTransfers->setCurrentWidget(ui->pUpdated);
                if (!mWaiting && !mIndexing)
                {
                    overlay->setVisible(true);
                }
                else
                {
                    overlay->setVisible(false);
                }
            }
        }
    }
    setFooterState();
    updateBlockedState();
}

void InfoDialog::on_bSettings_clicked()
{
    emit userActivity();

    QPoint p = ui->bSettings->mapToGlobal(QPoint(ui->bSettings->width() - 2, ui->bSettings->height()));

#ifdef __APPLE__
    QPointer<InfoDialog> iod = this;
#endif

    app->showTrayMenu(&p);

#ifdef __APPLE__
    if (!iod)
    {
        return;
    }

    if (!this->rect().contains(this->mapFromGlobal(QCursor::pos())))
    {
        this->hide();
    }
#endif

    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(AppStatsEvents::EventType::MENU_CLICKED, true);
}

void InfoDialog::on_bUpgrade_clicked()
{
    auto hasOffer = mDiscountPolicy && mDiscountPolicy->isCampaignActive();

    if (!hasOffer)
    {
        MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
            AppStatsEvents::EventType::UPGRADE_CLICKED_INFO_DIALOG,
            true);
        Utilities::upgradeClicked();
    }
    else
    {
        MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
            AppStatsEvents::EventType::TARGETED_DISCOUNT_INFODIALOG_BUTTON_CLICKED,
            true);
        emit requestShowDiscountDialog();
    }
}

void InfoDialog::on_bUpgradeOverDiskQuota_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::UPGRADE_CLICKED_INFO_DIALOG_OVER_QUOTA_WARNING,
        true);
    Utilities::upgradeClicked();
}

void InfoDialog::openFolder(QString path)
{
    Utilities::openUrl(QUrl::fromLocalFile(path));
}

void InfoDialog::addSync(SyncInfo::SyncOrigin origin, mega::MegaHandle handle)
{
    // Qt5-ONLY workaround, Wayland only. On Wayland, pass the InfoDialog as
    // parent so the dialog (and any modal child it spawns, e.g. exclusions) is
    // anchored to a real window — otherwise it is rootless and the compositor
    // never presents it (it only appears in the taskbar). On X11 the rootless
    // dialog maps fine, so keep the original null parent there.
    // TODO Qt6: drop the explicit parent — Qt6's Wayland backend presents the
    // dialog without it (the Qt6 tree calls addSync(origin, handle)).
    QWidget* const parent = Platform::getInstance()->isWayland() ? this : nullptr;
    CreateRemoveSyncsManager::addSync(origin, handle, QString(), parent);
}

void InfoDialog::addBackup(SyncInfo::SyncOrigin origin)
{
    // Qt5-ONLY workaround, Wayland only. See addSync(): on Wayland anchor the
    // dialog to a real window so it is presented instead of only appearing in
    // the taskbar; on X11 keep the original null parent.
    // TODO Qt6: drop the explicit parent (the Qt6 tree calls addBackup(origin)).
    QWidget* const parent = Platform::getInstance()->isWayland() ? this : nullptr;
    CreateRemoveBackupsManager::addBackup(origin, QStringList(), parent);
    // Qt5-ONLY workaround. On Wayland, keep the InfoDialog mapped: it anchors the
    // backups dialog (see eventFilter). Hiding it would unmap the anchor and the
    // compositor would never present the backups dialog.
    // TODO Qt6: drop the !Platform::getInstance()->isWayland() guard — restore the
    // unconditional hide() the Qt6 tree uses.
    if (CreateRemoveBackupsManager::isBackupsDialogOpen() && !Platform::getInstance()->isWayland())
    {
        hide();
    }
}

void InfoDialog::onOverlayClicked()
{
    app->uploadActionClicked(AppStatsEvents::EventType::INFO_DIALOG_UPLOAD_CLICKED);
}

void InfoDialog::onTransferManagerClicked()
{
    emit userActivity();
    app->transferManagerActionClicked();
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::OPEN_TRANSFER_MANAGER_CLICKED,
        true);
}

void InfoDialog::onAddSyncClicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::INFO_DIALOG_ADD_SYNC_CLICKED,
        true);
    addSync(SyncInfo::SyncOrigin::INFODIALOG_BUTTON_ORIGIN);
}

void InfoDialog::clearUserAttributes()
{
    ui->bAvatar->clearData();
}

bool InfoDialog::updateOverStorageState(int state)
{
    if (storageState != state)
    {
        storageState = state;
        updateDialogState();
        return true;
    }
    return false;
}

void InfoDialog::onUnseenAlertsChanged(const UnseenUserMessagesMap& alerts)
{
    setUnseenNotifications(alerts[MessageType::ALL]);
    filterMenu->setUnseenNotifications(alerts[MessageType::ALL],
                                       alerts[MessageType::ALERT_CONTACTS],
                                       alerts[MessageType::ALERT_SHARES],
                                       alerts[MessageType::ALERT_PAYMENTS]);
    ui->wSortNotifications->resetAllFilterHasBeenSelected();
}

void InfoDialog::reset()
{
    notificationsReady = false;
    ui->sNotifications->setCurrentWidget(ui->pNoNotifications);
    ui->wSortNotifications->setActualFilter(MessageType::ALL);

    hideSomeIssues();

    setUnseenNotifications(0);
    if (filterMenu)
    {
        filterMenu->reset();
    }

    transferOverquotaAlertEnabled = false;
    transferAlmostOverquotaAlertEnabled = false;
    transferQuotaState = QuotaState::OK;
}

void InfoDialog::setPSAannouncement(int id, QString title, QString text, QString urlImage, QString textButton, QString linkButton)
{
    ui->wPSA->setAnnounce(id, title, text, urlImage, textButton, linkButton);
}

void InfoDialog::enterBlockingState()
{
    enableUserActions(false);
    ui->bTransferManager->setPauseEnabled(false);
    ui->wTabOptions->setVisible(false);
    mTransferScanCancelUi->show();
    updateState();
}

void InfoDialog::leaveBlockingState(bool fromCancellation)
{
    enableUserActions(true);
    ui->bTransferManager->setPauseEnabled(true);
    ui->wTabOptions->setVisible(true);
    mTransferScanCancelUi->hide(fromCancellation);
    updateState();
}

void InfoDialog::disableCancelling()
{
    mTransferScanCancelUi->disableCancelling();
}

void InfoDialog::setUiInCancellingStage()
{
    mTransferScanCancelUi->setInCancellingStage();
}

void InfoDialog::updateUiOnFolderTransferUpdate(const FolderTransferUpdateEvent &event)
{
    mTransferScanCancelUi->onFolderTransferUpdate(event);
}

bool InfoDialog::event(QEvent* event)
{
    if (event->type() == QEvent::LanguageChange)
    {
        ui->retranslateUi(this);
        updateUpgradeButtonText();
        updateCreateSyncButtonText();
    }
    else if (event->type() == ThemeManager::ThemeChanged && mPreferences->logged())
    {
        setUsage();
    }
    return QDialog::event(event);
}

bool InfoDialog::eventFilter(QObject *obj, QEvent *e)
{
    if (obj == ui->wStorageUsage && e->type() == QEvent::MouseButtonPress)
    {
        onStorageDetailsClicked();
        return true;
    }

#ifdef Q_OS_LINUX
    static bool firstime = true;
    if (qEnvironmentVariableIsSet("START_MEGASYNC_MINIMIZED") && firstime &&
        (obj == this && e->type() == QEvent::Paint))
    {
        MegaApi::log(MegaApi::LOG_LEVEL_DEBUG, QString::fromUtf8("Minimizing info dialog (reason: %1)...").arg(e->type()).toUtf8().constData());
        showMinimized();
        firstime = false;
    }

    if (doNotActAsPopup)
    {
        if (obj == this && e->type() == QEvent::Close)
        {
            e->ignore(); //This prevents the dialog from closing
            app->tryExitApplication();
            return true;
        }
    }
    else if (obj == this)
    {
        // Qt5-ONLY: on Wayland, keep the InfoDialog mapped ONLY while it is
        // anchoring a dialog parented to it (the ones launched from its menu —
        // add sync, add backup, ... — which we pass `this` as parent on
        // Wayland); unmapping the anchor would stop the compositor presenting
        // them. Unrelated top-level dialogs (e.g. the paste-link dialog, which
        // has no parent) must NOT keep it open. In every other case — X11, or
        // Wayland with no child dialog open — close it on deactivate/focus-out.
        // TODO Qt6: remove keepOpenAsAnchor and restore the plain hide — Qt6
        // presents the dialogs without keeping the InfoDialog mapped.
        const bool keepOpenAsAnchor =
            Platform::getInstance()->isWayland() && DialogOpener::isAnyDialogChildOf(this);
        if (!keepOpenAsAnchor &&
            (e->type() == QEvent::WindowDeactivate || e->type() == QEvent::FocusOut))
        {
            hide();
            return true;
        }
    }

#endif

#ifdef __APPLE__
    if (obj == this)
    {
        if (QOperatingSystemVersion::current() <=
            QOperatingSystemVersion::OSXMavericks) // manage spontaneus mouse press events
        {
            if (e->type() == QEvent::MouseButtonPress && e->spontaneous())
            {
                return true;
            }
        }

        if (e->type() == QEvent::WindowDeactivate)
        {
            hide();
            return true;
        }
    }
#endif

    return QDialog::eventFilter(obj, e);
}

void InfoDialog::onStorageDetailsClicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::STORAGE_USAGE_CLICKED_INFO_DIALOG);
    auto dialog = new AccountDetailsDialog();
    DialogOpener::showNonModalDialog<AccountDetailsDialog>(dialog);
}

void InfoDialog::resetLoggedInMode()
{
    loggedInMode = STATE_NONE;
}

void InfoDialog::on_tTransfers_clicked()
{
    ui->tTransfers->setProperty("class", QString::fromUtf8("button-brand"));
    ui->tTransfers->style()->unpolish(ui->tTransfers);
    ui->tTransfers->style()->polish(ui->tTransfers);

    // Deselct the other tab
    ui->tNotifications->setProperty("class", QString());
    ui->tNotifications->style()->unpolish(ui->tNotifications);
    ui->tNotifications->style()->polish(ui->tNotifications);

    ui->lTransfers->setProperty("state", QString::fromUtf8("selected"));
    ui->lTransfers->style()->unpolish(ui->lTransfers);
    ui->lTransfers->style()->polish(ui->lTransfers);

    ui->lRecents->setProperty("state", QString::fromUtf8("normal"));
    ui->lRecents->style()->unpolish(ui->lRecents);
    ui->lRecents->style()->polish(ui->lRecents);

    ui->sTabs->setCurrentWidget(ui->pTransfersTab);
    setFooterState();

    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(AppStatsEvents::EventType::TRANSFER_TAB_CLICKED,
                                                          sender(), ui->tTransfers, true);
    ui->tNotifications->setCursor(Qt::PointingHandCursor);
    ui->tTransfers->setCursor(Qt::ArrowCursor);
}

void InfoDialog::on_tNotifications_clicked()
{
    ui->tNotifications->setProperty("class", QString::fromUtf8("button-brand"));
    ui->tNotifications->style()->unpolish(ui->tNotifications);
    ui->tNotifications->style()->polish(ui->tNotifications);

    // Deselect the other tab
    ui->tTransfers->setProperty("class", QString());
    ui->tTransfers->style()->unpolish(ui->tTransfers);
    ui->tTransfers->style()->polish(ui->tTransfers);

    app->getNotificationController()->requestNotifications();
    ui->lRecents->setProperty("state", QString::fromUtf8("selected"));
    ui->lRecents->style()->unpolish(ui->lRecents);
    ui->lRecents->style()->polish(ui->lRecents);
    ui->lTransfers->setProperty("state", QString::fromUtf8("normal"));

    ui->lTransfers->style()->unpolish(ui->lTransfers);
    ui->lTransfers->style()->polish(ui->lTransfers);

    ui->sTabs->setCurrentWidget(ui->pNotificationsTab);
    setFooterState();

    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(AppStatsEvents::EventType::NOTIFICATION_TAB_CLICKED, true);
    ui->tTransfers->setCursor(Qt::PointingHandCursor);
    ui->tNotifications->setCursor(Qt::ArrowCursor);
}

void InfoDialog::onActualFilterClicked()
{
    if (!notificationsReady || !filterMenu)
    {
        return;
    }
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::NOTIFICATION_FILTERS_OPENED,
        true);
    QPoint p = ui->wFilterAndSettings->mapToGlobal(QPoint(4, 4));
    filterMenu->move(p);
    filterMenu->show();
}

void InfoDialog::applyFilterOption(MessageType opt)
{
    if (filterMenu && filterMenu->isVisible())
    {
        filterMenu->hide();
    }
    AppStatsEvents::EventType event = AppStatsEvents::EventType::NOTIFICATION_FILTER_SELECTED_ALL;
    switch (opt)
    {
        case MessageType::ALL:
            event = AppStatsEvents::EventType::NOTIFICATION_FILTER_SELECTED_ALL;
            break;
        case MessageType::ALERT_SHARES:
            event = AppStatsEvents::EventType::NOTIFICATION_FILTER_SELECTED_INCOMING_SHARES;
            break;
        case MessageType::ALERT_CONTACTS:
            event = AppStatsEvents::EventType::NOTIFICATION_FILTER_SELECTED_CONTACTS;
            break;
        case MessageType::ALERT_PAYMENTS:
            event = AppStatsEvents::EventType::NOTIFICATION_FILTER_SELECTED_PAYMENTS;
            break;
        default:
            break;
    }
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(event, true);
    applyNotificationFilter(opt);
}

void InfoDialog::on_bNotificationsSettings_clicked()
{
    MegaSyncApp->getStatsEventHandler()->sendTrackedEvent(
        AppStatsEvents::EventType::NOTIFICATION_SETTINGS_CLICKED,
        true);
    MegaSyncApp->openSettings(SettingsDialog::NOTIFICATIONS_TAB);
}

void InfoDialog::on_bDiscard_clicked()
{
    if(transferQuotaState == QuotaState::FULL || transferQuotaState == QuotaState::OVERQUOTA)
    {
        transferOverquotaAlertEnabled = false;
        emit transferOverquotaMsgVisibilityChange(transferOverquotaAlertEnabled);
    }
    else if(transferQuotaState == QuotaState::WARNING)
    {
        transferAlmostOverquotaAlertEnabled = false;
        emit almostTransferOverquotaMsgVisibilityChange(transferAlmostOverquotaAlertEnabled);
    }

    if(storageState == Preferences::STATE_ALMOST_OVER_STORAGE ||
            storageState == Preferences::STATE_OVER_STORAGE)
    {
        updateOverStorageState(Preferences::STATE_OVER_STORAGE_DISMISSED);
        emit dismissStorageOverquota(overQuotaState);
    }
    else
    {
        updateDialogState();
    }
}

void InfoDialog::on_bBuyQuota_clicked()
{
    on_bUpgrade_clicked();
}

void InfoDialog::sTabsChanged(int tab)
{
    static int lastTab = -1;
    if (tab != ui->sTabs->indexOf(ui->pNotificationsTab)
            && lastTab == ui->sTabs->indexOf(ui->pNotificationsTab)
            && ui->wSortNotifications->allFilterHasBeenSelected())
    {
        app->getNotificationController()->ackSeenUserMessages();
        ui->wSortNotifications->resetAllFilterHasBeenSelected();
    }
    lastTab = tab;
}

void InfoDialog::hideSomeIssues()
{
    mShownSomeIssuesOccurred = false;
    ui->wSomeIssuesOccurred->hide();
}

void InfoDialog::showSomeIssues()
{
    if (mShownSomeIssuesOccurred)
    {
        return;
    }

    ui->wSomeIssuesOccurred->show();
    animationGroupSomeIssues.start();
    mShownSomeIssuesOccurred = true;
}

void InfoDialog::updateUpgradeButtonText()
{
    updateUpgradeButtonState();
}

void InfoDialog::updateCreateSyncButtonText()
{
    // We add a space to keep a distance from the button icon
    const QString label = QString::fromLatin1(" ") + tr("Add sync");
    ui->bCreateSync->setText(label);
}

void InfoDialog::on_bDismissSyncSettings_clicked()
{
    mSyncInfo->dismissUnattendedDisabledSyncs(mega::MegaSync::TYPE_TWOWAY);
}

void InfoDialog::on_bOpenSyncSettings_clicked()
{
    MegaSyncApp->openSettings(SettingsDialog::SYNCS_TAB);
    mSyncInfo->dismissUnattendedDisabledSyncs(mega::MegaSync::TYPE_TWOWAY);
}

void InfoDialog::on_bDismissBackupsSettings_clicked()
{
    mSyncInfo->dismissUnattendedDisabledSyncs(mega::MegaSync::TYPE_BACKUP);
}

void InfoDialog::on_bOpenBackupsSettings_clicked()
{
    MegaSyncApp->openSettings(SettingsDialog::BACKUP_TAB);
    mSyncInfo->dismissUnattendedDisabledSyncs(mega::MegaSync::TYPE_BACKUP);
}

void InfoDialog::on_bDismissAllSyncsSettings_clicked()
{
    mSyncInfo->dismissUnattendedDisabledSyncs(SyncInfo::AllHandledSyncTypes);
}

void InfoDialog::on_bOpenAllSyncsSettings_clicked()
{
    MegaSyncApp->openSettings(SettingsDialog::SYNCS_TAB);
    mSyncInfo->dismissUnattendedDisabledSyncs(SyncInfo::AllHandledSyncTypes);
}

int InfoDialog::getLoggedInMode() const
{
    return loggedInMode;
}

void InfoDialog::showNotifications()
{
    on_tNotifications_clicked();
}

void InfoDialog::move(int x, int y)
{
   qtBugFixer.onStartMove();
   QDialog::move(x, y);
}

void InfoDialog::setUnseenNotifications(long long value)
{
    assert(value >= 0);

    if (value > 0)
    {
        ui->bNumberUnseenNotifications->setText(QString::number(value));
        ui->bNumberUnseenNotifications->show();
    }
    else
    {
        ui->bNumberUnseenNotifications->hide();
    }
}

void InfoDialog::updateHeaderBackground()
{
    auto isHeaderTransparent(ui->wHeader->property(TRANSPARENT_HEADER).toBool());
    auto willBeHeaderTransparent(false);

    if (mState == StatusInfo::TRANSFERS_STATES::STATE_INDEXING)
    {
        // If the PSA is not visible and the app is scanning > the header is transparent;
        willBeHeaderTransparent = !ui->wPSA->isPSAshown();
    }

    // The background state has changed
    if (willBeHeaderTransparent != isHeaderTransparent)
    {
        ui->wHeader->setProperty(TRANSPARENT_HEADER, willBeHeaderTransparent);
        setStyleSheet(styleSheet());
    }
}

void InfoDialog::changePSAVisibility(bool state)
{
    // Don´t show it when the scanning is working
    if (state && mTransferScanCancelUi && mTransferScanCancelUi->isActive())
    {
        state = false;
    }

    if (ui->wPSA->isPSAshown() != state)
    {
        state ? ui->wPSA->showPSA() : ui->wPSA->hidePSA();
        updateHeaderBackground();
    }
}

double InfoDialog::computeRatio(long long completed, long long remaining)
{
    return static_cast<double>(completed) / static_cast<double>(remaining);
}

void InfoDialog::enableUserActions(bool newState)
{
    ui->bAvatar->setEnabled(newState);
    ui->bUpgrade->setEnabled(newState);
    ui->bCreateSync->setEnabled(newState);
}

void InfoDialog::changeStatusState(StatusInfo::TRANSFERS_STATES newState)
{
    if (mState != newState)
    {
        mState = newState;
    }
}

void InfoDialog::setTransferManager(TransferManager *transferManager)
{
    mTransferManager = transferManager;
    mTransferManager->setTransferState(mState);
}

void InfoDialog::fixMultiscreenResizeBug(int& posX, int& posY)
{
    // An issue occurred with certain multiscreen setup that caused Qt to missplace the info dialog.
    // This works around that by ensuring infoDialog does not get incorrectly resized. in which case,
    // it is reverted to the correct size.

    ensurePolished();
    auto initialDialogWidth  = width();
    auto initialDialogHeight = height();
    QTimer::singleShot(1, this, [this, initialDialogWidth, initialDialogHeight, posX, posY](){
        if (width() > initialDialogWidth || height() > initialDialogHeight) //miss scaling detected
        {
            MegaApi::log(MegaApi::LOG_LEVEL_ERROR,
                         QString::fromUtf8("A dialog. New size = %1,%2. should be %3,%4 ")
                         .arg(width()).arg(height()).arg(initialDialogWidth).arg(initialDialogHeight)
                         .toUtf8().constData());

            resize(initialDialogWidth,initialDialogHeight);

            auto iDPos = pos();
            if (iDPos.x() != posX || iDPos.y() != posY )
            {
                MegaApi::log(MegaApi::LOG_LEVEL_ERROR,
                             QString::fromUtf8("Missplaced info dialog. New pos = %1,%2. should be %3,%4 ")
                             .arg(iDPos.x()).arg(iDPos.y()).arg(posX).arg(posY)
                             .toUtf8().constData());
                move(posX, posY);

                QTimer::singleShot(1, this, [this, initialDialogWidth, initialDialogHeight](){
                    if (width() > initialDialogWidth || height() > initialDialogHeight) //miss scaling detected
                    {
                        MegaApi::log(MegaApi::LOG_LEVEL_ERROR,
                                     QString::fromUtf8("Missscaled info dialog after second move. New size = %1,%2. should be %3,%4 ")
                                     .arg(width()).arg(height()).arg(initialDialogWidth).arg(initialDialogHeight)
                                     .toUtf8().constData());

                        resize(initialDialogWidth,initialDialogHeight);
                    }
                });
            }
        }
    });
}

void InfoDialog::repositionInfoDialog()
{
    int posx, posy;
    Platform::getInstance()->calculateInfoDialogCoordinates(rect(), &posx, &posy);

    fixMultiscreenResizeBug(posx, posy);

    MegaApi::log(MegaApi::LOG_LEVEL_DEBUG, QString::fromUtf8("Moving Info Dialog to posx = %1, posy = %2")
                 .arg(posx)
                 .arg(posy)
                 .toUtf8().constData());

    if(posx != this->x() || posy != this->y())
    {
        move(posx, posy);
    }
}

void InfoDialog::initNotificationArea()
{
    mNotificationsViewHoverManager.setView(ui->tvNotifications);

    ui->tvNotifications->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
    ui->tvNotifications->verticalScrollBar()->setSingleStep(12);
    ui->tvNotifications->setModel(app->getNotificationController()->getModel());
    ui->tvNotifications->sortByColumn(0, Qt::AscendingOrder);
    auto delegate = new UserMessageDelegate(app->getNotificationController()->getModel(),
                                            ui->tvNotifications);
    ui->tvNotifications->setItemDelegate(delegate);

    applyNotificationFilter(MessageType::ALL);
    connect(app->getNotificationController(), &UserMessageController::userMessagesReceived, this, [this]()
    {
        // We need to check if there is any user message to display or not
        // with the actual selected filter.
        applyNotificationFilter(filterMenu->getCurrentFilter());
    });

    notificationsReady = true;
}

void InfoDialog::applyNotificationFilter(MessageType opt)
{
    switch (opt)
    {
        case MessageType::ALERT_CONTACTS:
        {
            ui->wSortNotifications->setActualFilter(opt);

            if (app->getNotificationController()->hasElementsOfType(MessageType::ALERT_CONTACTS))
            {
                ui->sNotifications->setCurrentWidget(ui->pNotifications);
            }
            else
            {
                ui->lNoNotifications->setText(tr("No notifications for contacts"));
                ui->sNotifications->setCurrentWidget(ui->pNoNotifications);
            }

            break;
        }
        case MessageType::ALERT_SHARES:
        {
            ui->wSortNotifications->setActualFilter(opt);

            if (app->getNotificationController()->hasElementsOfType(MessageType::ALERT_SHARES))
            {
                ui->sNotifications->setCurrentWidget(ui->pNotifications);
            }
            else
            {
                ui->lNoNotifications->setText(tr("No notifications for incoming shares"));
                ui->sNotifications->setCurrentWidget(ui->pNoNotifications);
            }

            break;
        }
        case MessageType::ALERT_PAYMENTS:
        {
            ui->wSortNotifications->setActualFilter(opt);

            if (app->getNotificationController()->hasElementsOfType(MessageType::ALERT_PAYMENTS))
            {
                ui->sNotifications->setCurrentWidget(ui->pNotifications);
            }
            else
            {
                ui->lNoNotifications->setText(tr("No notifications for payments"));
                ui->sNotifications->setCurrentWidget(ui->pNoNotifications);
            }
            break;
        }
        case MessageType::ALL:
        case MessageType::ALERT_TAKEDOWNS:
        default:
        {
            ui->wSortNotifications->setActualFilter(opt);

            if (app->getNotificationController()->hasNotifications())
            {
                ui->sNotifications->setCurrentWidget(ui->pNotifications);
            }
            else
            {
                ui->lNoNotifications->setText(tr("No notifications"));
                ui->sNotifications->setCurrentWidget(ui->pNoNotifications);
            }
            break;
        }
    }
    setFooterState();
    app->getNotificationController()->applyFilter(opt);
}

void InfoDialog::setFooterState()
{
    bool hasTransfers = false;
    auto currentTab = ui->sTabs->currentWidget();
    if (currentTab == ui->pNotificationsTab)
    {
        hasTransfers = ui->sNotifications->currentWidget() == ui->pNotifications;
    }
    else
    {
        auto transfersCount = app->getTransfersModel()->getTransfersCount();
        hasTransfers = (ui->sActiveTransfers->currentWidget() == ui->pTransfers &&
                        (transfersCount.totalDownloads || transfersCount.totalUploads));
    }
    ui->wBottom->setProperty("hasTransfers", hasTransfers);
    ui->wBottom->style()->unpolish(ui->wBottom);
    ui->wBottom->style()->polish(ui->wBottom);
    ui->wBottom->update();
    ui->wStatus->setPropertyAndPropagateToChildren("hasTransfers", hasTransfers);
    ui->wSeparator->setVisible(hasTransfers);
}

void InfoDialog::onTopTransferTypeChanged(TransferData::TransferTypes type)
{
    ui->bTransferManager->setTopTransferDirection(type & TransferData::TRANSFER_UPLOAD);
}

void InfoDialog::showStalledIssuesDialog()
{
    MegaApplication::showStalledIssuesDialog();
}

void InfoDialog::updateUpgradeButtonState()
{
    if (!mPreferences->logged())
    {
        return;
    }
    const bool hasOffer = mDiscountPolicy && mDiscountPolicy->isCampaignActive();
    const QuotaState quotaState = MegaSyncApp->getTransferQuota()->quotaState();
    const bool isTransferOverquota = (quotaState != QuotaState::OK);

    ui->bUpgrade->setProperty("type", hasOffer ? QLatin1String("brand") : QLatin1String("primary"));
    ui->bUpgrade->setProperty("state", hasOffer ? QLatin1String("offer") : QLatin1String("normal"));

    ui->bUpgrade->setVisible(hasOffer ||
                             Utilities::shouldDisplayUpgradeButton(isTransferOverquota));
    ui->bCreateSync->setVisible(!hasOffer);

    if (hasOffer)
    {
        ui->bUpgrade->setText(tr("%1% off %2")
                                  .arg(mDiscountPolicy->getPercentage())
                                  .arg(mDiscountPolicy->getPlanName(false)));
    }
    else
    {
        ui->bUpgrade->setText(QCoreApplication::translate("SettingsDialog", "Upgrade"));
    }
    ui->bUpgrade->style()->polish(ui->bUpgrade);
}

void InfoDialog::setDiscountPolicy(QPointer<DiscountPolicy> policy)
{
    if (!policy.isNull())
    {
        mDiscountPolicy = policy;
        connect(mDiscountPolicy,
                &DiscountPolicy::campaignActivated,
                this,
                &InfoDialog::updateUpgradeButtonState,
                Qt::UniqueConnection);
        connect(mDiscountPolicy,
                &DiscountPolicy::campaignDeactivated,
                this,
                &InfoDialog::updateUpgradeButtonState,
                Qt::UniqueConnection);
        updateUpgradeButtonText();
    }
}
