#include "AbstractPlatform.h"

#include "DialogOpener.h"
#include "megaapi.h"
#include "MultiQFileDialog.h"
#include "TokenParserWidgetManager.h"

#include <QCursor>
#include <QGuiApplication>
#include <QScreen>
#include <QWindow>

using namespace mega;

void AbstractPlatform::prepareForSync()
{

}

bool AbstractPlatform::enableTrayIcon(QString /*executable*/)
{
    return false;
}

void AbstractPlatform::unHideTrayIcon() {}

bool AbstractPlatform::isTilingWindowManager()
{
    return false;
}

bool AbstractPlatform::isWayland()
{
    // When a GUI application exists, the platform plugin name is authoritative.
    // (Empty before the app is created — only the Linux override needs the
    // pre-QApplication environment fallback, since Wayland is Linux-only.)
    return QGuiApplication::platformName().startsWith(QLatin1String("wayland"));
}

void AbstractPlatform::moveDialog(QWidget* dialog, const QPoint& pos, QWindow* /*visualParent*/)
{
    dialog->move(pos);
}

QPoint AbstractPlatform::initialDialogPosition(const QSize& dialogSize) const
{
    // Without a parent to center on, prefer the screen the user is working on
    // (under the cursor, then the active window) instead of always falling back
    // to the primary monitor, which would otherwise make dialogs jump to the
    // primary screen on multi-monitor setups.
    auto screen = QGuiApplication::screenAt(QCursor::pos());
    if (!screen)
    {
        if (auto activeWindow = QGuiApplication::focusWindow())
        {
            screen = activeWindow->screen();
        }
    }
    if (!screen)
    {
        screen = QGuiApplication::primaryScreen();
    }
    if (!screen)
    {
        return QPoint();
    }

    const auto screenGeometry = screen->geometry();
    return QPoint(screenGeometry.x() + (screenGeometry.width() - dialogSize.width()) / 2,
                  screenGeometry.y() + (screenGeometry.height() - dialogSize.height()) / 2);
}

QPoint AbstractPlatform::initialDialogPosition(const QSize& dialogSize,
                                               const QRect& parentGeometry) const
{
    if (!parentGeometry.isValid())
    {
        return initialDialogPosition(dialogSize);
    }

    return QPoint(parentGeometry.x() + (parentGeometry.width() - dialogSize.width()) / 2,
                  parentGeometry.y() + (parentGeometry.height() - dialogSize.height()) / 2);
}

QByteArray AbstractPlatform::encrypt(QByteArray data, QByteArray /*key*/)
{
    return data;
}

QByteArray AbstractPlatform::decrypt(QByteArray data, QByteArray /*key*/)
{
    return data;
}

QByteArray AbstractPlatform::getLocalStorageKey()
{
    return QByteArray(128, 0);
}

QString AbstractPlatform::getDefaultFileBrowserApp()
{
    return QString();
}

QString AbstractPlatform::getDefaultOpenAppByMimeType(QString /*mimeType*/)
{
    return QString();
}

bool AbstractPlatform::getValue(const char * const /*name*/, const bool /*default_value*/)
{
    return false;
}

std::string AbstractPlatform::getValue(const char * const /*name*/, const std::string& /*default_value*/)
{
    return std::string("");
}

QString AbstractPlatform::getWindowManagerName()
{
    return QString();
}

void AbstractPlatform::enableDialogBlur(QDialog* /*dialog*/)
{
}

void AbstractPlatform::uninstall()
{
}

void AbstractPlatform::initMenu(QMenu *m, const char *objectName, const bool applyDefaultStyling)
{
    if (m)
    {
        m->setObjectName(QString::fromUtf8(objectName));
        if (applyDefaultStyling)
        {
            m->setStyleSheet(QLatin1String("QMenu {"
                                           "background: #ffffff;"
                                           "padding-top: 6px;"
                                           "padding-bottom: 6px;"
                                           "border: 1px solid #B8B8B8;"
                                           "}"
                                           "QMenu::separator {"
                                           "height: 1px;"
                                           "margin: 6px 10px 6px 10px;"
                                           "background-color: rgba(0, 0, 0, 0.1);"
                                           "}"
                                           // For vanilla QMenus (only in TransferManager and
                                           // NodeSelectorTreeView (NodeSelector))
                                           "QMenu::item {"
                                           "font-size: 14px;"
                                           "margin: 6px 16px 6px 16px;"
                                           "color: #777777;"
                                           "padding-right: 16px;"
                                           "}"
                                           "QMenu::item:selected {"
                                           "color: #000000;"
                                           "}"
                                           // For menus with MenuItemActions
                                           "QLabel {"
                                           "font-size: 14px;"
                                           "padding: 0px;"
                                           "}"));
            m->ensurePolished();
        }
    }
}

QString AbstractPlatform::getSizeStringLocalizedOSbased(qint64 bytes)
{
    QString language = ((MegaApplication*)qApp)->getCurrentLanguageCode();
    QLocale locale(language);
    return locale.formattedDataSize(bytes, 2, QLocale::DataSizeFormat::DataSizeTraditionalFormat);
}

qint64 AbstractPlatform::getBaseUnitsSize() const
{
    constexpr qint64 base = 1024;

    return base;
}

void AbstractPlatform::fileSelector(const SelectorInfo &info)
{
    auto previousFileUploadSelector = DialogOpener::findDialog<QFileDialog>();
    QString defaultDir = info.defaultDir;
    if(previousFileUploadSelector)
    {
        defaultDir = previousFileUploadSelector->getDialog()->directory().path();
    }

    QPointer<QFileDialog> fileDialog = new QFileDialog(info.parent);
    fileDialog->setWindowTitle(info.title);
    fileDialog->setDirectory(defaultDir);
    fileDialog->setOption(QFileDialog::DontResolveSymlinks, true);
    fileDialog->setOption(QFileDialog::DontUseNativeDialog, false);
    if(info.multiSelection)
    {
        fileDialog->setFileMode(QFileDialog::ExistingFiles);
    }
    else
    {
        fileDialog->setFileMode(QFileDialog::ExistingFile);
    }
    //Orphan native dialogs must be modal in Windows and Linux. On macOS this method has its own implementation.
    if(!info.parent)
    {
        fileDialog->setModal(true);
    }

    TokenParserWidgetManager::styleQFileDialog(fileDialog);

    auto& func = info.func;
    DialogOpener::showDialog<QFileDialog>(fileDialog, [fileDialog, func]()
    {
        QStringList files;
        if(fileDialog->result() == QDialog::Accepted)
        {
            files = fileDialog->selectedFiles();
        }
        func(files);
    });
}

void AbstractPlatform::folderSelector(const SelectorInfo &info)
{
    auto previousFileUploadSelector = DialogOpener::findDialog<QFileDialog>();
    QString defaultDir = info.defaultDir;
    if(previousFileUploadSelector)
    {
        defaultDir = previousFileUploadSelector->getDialog()->directory().path();
    }

    auto& func = info.func;

    if(!info.multiSelection)
    {
        QPointer<QFileDialog> fileDialog = new QFileDialog(info.parent);
        fileDialog->setWindowTitle(info.title);
        fileDialog->setDirectory(defaultDir);
        fileDialog->setOption(QFileDialog::DontResolveSymlinks, true);
        fileDialog->setOption(QFileDialog::DontUseNativeDialog, false);
        fileDialog->setOption(QFileDialog::ShowDirsOnly, true);
        fileDialog->setFileMode(QFileDialog::Directory);
        //Orphan native dialogs must be modal in Windows and Linux. On macOS this method has its own implementation.
        if(!info.parent)
        {
            fileDialog->setModal(true);
        }

        TokenParserWidgetManager::styleQFileDialog(fileDialog);

        DialogOpener::showDialog<QFileDialog>(fileDialog, [fileDialog, func]()
        {
            QStringList files;
            if(fileDialog->result() == QDialog::Accepted)
            {
                files = fileDialog->selectedFiles();
            }
            func(files);
        });
    }
    else
    {
        QPointer<MultiQFileDialog> multiUploadFileDialog =
            new MultiQFileDialog(info.parent, info.title, defaultDir, info.multiSelection);
        multiUploadFileDialog->setOption(QFileDialog::DontResolveSymlinks, true);
        multiUploadFileDialog->setOption(QFileDialog::ShowDirsOnly, true);

        QPointer<QFileDialog> qfileDialogPointer =
            qobject_cast<QFileDialog*>(multiUploadFileDialog);
        TokenParserWidgetManager::styleQFileDialog(qfileDialogPointer);

        DialogOpener::showDialog<MultiQFileDialog>(multiUploadFileDialog, [multiUploadFileDialog, func](){
            QStringList files;
            if(multiUploadFileDialog->result() == QDialog::Accepted)
            {
                files = multiUploadFileDialog->selectedFiles();
            }
            func(files);
        });
    }
}

void AbstractPlatform::fileAndFolderSelector(const SelectorInfo &info)
{
    auto previousFileUploadSelector = DialogOpener::findDialog<MultiQFileDialog>();
    QString defaultDir = info.defaultDir;
     if(previousFileUploadSelector)
     {
         defaultDir = previousFileUploadSelector->getDialog()->directory().path();
     }

     QPointer<MultiQFileDialog> multiUploadFileDialog =
         new MultiQFileDialog(info.parent, info.title, defaultDir, info.multiSelection);

     multiUploadFileDialog->setOption(QFileDialog::DontResolveSymlinks, true);

     QPointer<QFileDialog> qfileDialogPointer = qobject_cast<QFileDialog*>(multiUploadFileDialog);
     TokenParserWidgetManager::styleQFileDialog(qfileDialogPointer);

     auto& func = info.func;
     DialogOpener::showDialog<MultiQFileDialog>(
         multiUploadFileDialog,
         [func, multiUploadFileDialog]()
         {
             QStringList files;
             if (multiUploadFileDialog->result() == QDialog::Accepted)
             {
                 files = multiUploadFileDialog->selectedFiles();
             }
             func(files);
         });
}

void AbstractPlatform::raiseFileFolderSelectors()
{
    DialogOpener::raiseAllDialogs();
}

void AbstractPlatform::closeFileFolderSelectors(QWidget *parent)
{

}

void AbstractPlatform::addSyncToLeftPane(QString /*syncPath*/, QString /*syncName*/, QString /*uuid*/)
{
}

void AbstractPlatform::removeAllSyncsFromLeftPane()
{
}

void AbstractPlatform::removeSyncFromLeftPane(QString /*syncPath*/) {}

QString AbstractPlatform::preparePathForSync(const QString& path)
{
    QDir dir(path);
    return QDir::toNativeSeparators(dir.canonicalPath());
}

bool AbstractPlatform::makePubliclyReadable(const QString& /*fileName*/)
{
    return false;
}

void AbstractPlatform::updateDisplayVersionAfterAutoUpdate(int /*versionCode*/, bool /*isPublic*/)
{}

bool AbstractPlatform::validateSystemTrayIntegration()
{
    return false;
}

std::shared_ptr<AbstractShellNotifier> AbstractPlatform::getShellNotifier()
{
    return mShellNotifier;
}

std::string AbstractPlatform::toLocalEncodedPath(const QString& path) const
{
    return path.toStdString();
}

QString AbstractPlatform::rectToString(const QRect &rect)
{
    return QString::fromUtf8("[%1,%2,%3,%4]").arg(rect.x()).arg(rect.y()).arg(rect.width()).arg(rect.height());
}

bool AbstractPlatform::loadRccResources(const QStringList& rccFiles)
{
    bool allLoaded = true;

    // We clear the icon caches so Qt can take the new icons from the recently reloaded rccs
    Utilities::clearIconCache();

    for (const QString& file: rccFiles)
    {
        if (!QFile::exists(file))
        {
            MegaApi::log(
                MegaApi::LOG_LEVEL_DEBUG,
                QString::fromUtf8("Missing resource file: %1").arg(file).toUtf8().constData());
            allLoaded = false;
            continue;
        }

        bool temp = QResource::unregisterResource(file);
        qDebug() << temp;

        if (!QResource::registerResource(file))
        {
            MegaApi::log(MegaApi::LOG_LEVEL_DEBUG,
                         QString::fromUtf8("Failed to register resource file: %1")
                             .arg(file)
                             .toUtf8()
                             .constData());
            allLoaded = false;
        }
        else
        {
            MegaApi::log(
                MegaApi::LOG_LEVEL_DEBUG,
                QString::fromUtf8("Registered resource file: %1").arg(file).toUtf8().constData());
        }
    }

    return allLoaded;
}

void AbstractPlatform::logInfoDialogCoordinates(const char *message, const QRect &screenGeometry, const QString &otherInformation)
{
    mega::MegaApi::log(
        mega::MegaApi::LOG_LEVEL_DEBUG,
        QString::fromUtf8("Calculating Info Dialog coordinates. %1: valid = %2, geom = %3, %4")
            .arg(QString::fromUtf8(message))
            .arg(screenGeometry.isValid())
            .arg(rectToString(screenGeometry), otherInformation)
            .toUtf8()
            .constData());
}

Preferences::SystemColorScheme AbstractPlatform::getCurrentThemeAppearance() const
{
    return {};
}

Preferences::ThemeAppeareance AbstractPlatform::getPanelTheme() const
{
    return {};
}

void AbstractPlatform::applyCurrentThemeOnCurrentDialogFrame(QWindow*) {}
