/*
 * Copyright (C) 2017 ~ 2026 Deepin Technology Co., Ltd.
 *
 * Author:     kirigaya <kirigaya@mkacg.com>
 * This program 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
 * any later version.
 *
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "widgetmanager.h"
#include "widgetmanager.h"
#include "ddlog.h"

#include <QDBusInterface>
#include <QDBusConnection>
#include <QDBusReply>

#define MAXPLAYER 5

WidgetManager *WidgetManager::Instance()
{
    qCDebug(app) << "Getting WidgetManager instance";
    static WidgetManager *instance = new WidgetManager;
    return instance;
}

void WidgetManager::setParents(QWidget *p)
{
    qCDebug(app) << "Setting parent widget for modules";
    m_fakerWidget = p;

#ifdef DISABLE_VIDEO
    // When DISABLE_VIDEO is defined, always skip video widgets regardless of videoFileList
    // because initVideoModule() returns empty widgets and videoFileList is populated at runtime
    qCDebug(app) << "Initializing desktop mode and icon modules (video disabled)";
    m_widgetList.append(initDesktopModeModule());
    m_widgetList.append(initIconModule());
    m_widgetList.currentVideo = 0;
    m_widgetList.current = m_widgetList.videoFileList.size();
#else
    if (!m_widgetList.videoFileList.isEmpty()) {
        qCDebug(app) << "Initializing video module with file:" << m_widgetList.videoFileList[0];
        m_widgetList.append(initVideoModule(m_widgetList.videoFileList[0]));
    } else {
        qCDebug(app) << "Initializing desktop mode and icon modules";
        m_widgetList.append(initDesktopModeModule());
        m_widgetList.append(initIconModule());
    }
    m_widgetList.currentVideo = 0;
    m_widgetList.current = 0;
#endif
}

int WidgetManager::getWidgetSize()
{
    return m_widgetList.videoFileList.size() + 2;
}

bool WidgetManager::canHandoff()
{
    if (m_widgetList.videoWidgetList.size() == MAXPLAYER)
        return true;
    if (m_widgetList.videoWidgetList.size() > m_widgetList.current+1)
        return true;
    return false;
}

void WidgetManager::finishedInit()
{
#ifndef DISABLE_VIDEO
    static_cast<VideoWidget *>(m_widgetList.videoWidgetList.last().w->getModel())->setFile(m_widgetList.videoFileList[m_widgetList.videoWidgetList.size()-1]);
#endif
}

void WidgetManager::delayInit()
{
    qCDebug(app) << "Delayed initialization started";

    int videoCount = m_widgetList.videoFileList.size();
    int widgetCount = m_widgetList.videoWidgetList.size();

    // First, create video widgets until we have enough or reach MAXPLAYER
    if (widgetCount < qMin(videoCount, MAXPLAYER)) {
        Q_ASSERT(widgetCount <= MAXPLAYER);
        Q_ASSERT(videoCount > m_widgetList.currentVideo + 1);

        qCDebug(app) << "Initializing next video module";
        m_widgetList.append(initVideoModule(m_widgetList.videoFileList[widgetCount]));
        return;
    }

    // After all video widgets are created, add the other widgets
    if (m_widgetList.otherWidgetList.isEmpty()) {
        m_widgetList.append(initDesktopModeModule());
        m_widgetList.append(initIconModule());
    }
}

void WidgetManager::slotShowMore()
{
    // disable temporarily
#if 0
    if (getCurrentWidget().type == videoType && m_widgetList.current == m_widgetList.videoFileList.size()-1) {
        emit showMore();
    }
#endif
}

IntroductionWidget WidgetManager::getCurrentWidget()
{
    if (m_widgetList.size && m_widgetList.current != -1) {
        if (m_widgetList.current < m_widgetList.videoFileList.size()) {
            // When DISABLE_VIDEO, videoWidgetList may be empty
            if (m_widgetList.currentVideo < m_widgetList.videoWidgetList.size()) {
                return m_widgetList.videoWidgetList.at(m_widgetList.currentVideo);
            }
            return IntroductionWidget();
        } else
            return m_widgetList.otherWidgetList.at(m_widgetList.current - m_widgetList.videoFileList.size());
    }

    return IntroductionWidget();
}

IntroductionWidget WidgetManager::handoffNextWidget()
{
    qCDebug(app) << "Handing off to next widget";
    auto lastWidget = getCurrentWidget();
    setVideoMute(lastWidget, true);

    m_widgetList.current++;
    if (m_widgetList.current < m_widgetList.videoFileList.size()) {
        int widgetCount = m_widgetList.videoWidgetList.size();
        if (m_widgetList.currentVideo + 1 >= widgetCount)
            m_widgetList.currentVideo = 0;
        else
            m_widgetList.currentVideo++;

        int index = m_widgetList.currentVideo + 1;
        if (index >= widgetCount)
            index = 0;
        if (m_widgetList.current < m_widgetList.videoFileList.size()-1) {
            BaseModuleWidget *nextVideo = m_widgetList.videoWidgetList.at(index).w;
            // Skip preload if target widget is the one currently displayed on screen
#ifndef DISABLE_VIDEO
            if (nextVideo != lastWidget.w) {
                static_cast<VideoWidget *>(nextVideo->getModel())->setFile(m_widgetList.videoFileList.at(m_widgetList.current+1));
            }
#endif
        }
    }

    auto introWidget = getCurrentWidget();
    setVideoMute(introWidget, false);
    return introWidget;
}

IntroductionWidget WidgetManager::handoffPrevWidget()
{
    qCDebug(app) << "Handing off to previous widget";
    Q_ASSERT(m_widgetList.current);
    auto lastWidget = getCurrentWidget();
    setVideoMute(lastWidget, true);

    m_widgetList.current--;
    if (m_widgetList.current + 1 < m_widgetList.videoFileList.size()) {
        int widgetCount = m_widgetList.videoWidgetList.size();
        if (!m_widgetList.currentVideo)
            m_widgetList.currentVideo = widgetCount - 1;
        else
            m_widgetList.currentVideo--;

        // handoffNextWidget may have overwritten this widget's file via ring-buffer preload
        BaseModuleWidget *curVideo = m_widgetList.videoWidgetList.at(m_widgetList.currentVideo).w;
#ifndef DISABLE_VIDEO
        static_cast<VideoWidget *>(curVideo->getModel())->setFile(m_widgetList.videoFileList.at(m_widgetList.current));
#endif

        int index = m_widgetList.currentVideo - 1;
        if (index < 0)
            index = widgetCount - 1;
        if (m_widgetList.current) {
            BaseModuleWidget *nextVideo = m_widgetList.videoWidgetList.at(index).w;
            // Skip preload if target widget is the one currently displayed on screen
#ifndef DISABLE_VIDEO
            if (nextVideo != lastWidget.w) {
                static_cast<VideoWidget *>(nextVideo->getModel())->setFile(m_widgetList.videoFileList.at(m_widgetList.current-1));
            }
#endif
        }
    }

    auto introWidget = getCurrentWidget();
    setVideoMute(introWidget, false);
    return introWidget;
}

WidgetManager::WidgetManager(QObject *parent)
    : QObject(parent)
{
    qCDebug(app) << "WidgetManager constructor called";
    initPageType();
}

IntroductionWidget WidgetManager::initVideoModule(QUrl file)
{
    qCDebug(app) << "Initializing video module with file:" << file;
    if (m_widgetList.videoWidgetList.size() >= MAXPLAYER)
        return IntroductionWidget();
    IntroductionWidget widget;
#ifndef DISABLE_VIDEO
    VideoWidget *module = new VideoWidget(file, m_fakerWidget);
    connect(module, &VideoWidget::showMore, this, &WidgetManager::slotShowMore);
    connect(module, &VideoWidget::finishedShow, [=](){
        module->setFile(file);
    });
    BaseModuleWidget *w = new BaseModuleWidget(module, true, m_fakerWidget);
    w->setFixedSize(QSize(m_fakerWidget->width() + 1, m_fakerWidget->height() + 1));
    w->hide();
    widget.w = w;
    widget.type = videoType;
#endif
    return widget;
}

IntroductionWidget WidgetManager::initDesktopModeModule()
{
    qCDebug(app) << "Initializing desktop mode module";
    IntroductionWidget widget;
    DesktopModeModule *module = new DesktopModeModule;
    module->updateBigIcon();
    BaseModuleWidget *w = new BaseModuleWidget(module, false, m_fakerWidget);
    w->setDescribe(tr("You can switch \"Mode\" by right-clicking on the dock."));
    w->setFixedSize(m_fakerWidget->size());
    w->hide();
    widget.w = w;
    widget.type = StyleType;
    return widget;
}

IntroductionWidget WidgetManager::initIconModule()
{
    qCDebug(app) << "Initializing icon module";
    IntroductionWidget widget;
    IconModule *module = new IconModule;
    module->updateBigIcon();
    BaseModuleWidget *w = new BaseModuleWidget(module, false, m_fakerWidget);
    w->setFixedSize(m_fakerWidget->size());
    w->hide();
    widget.w = w;
    widget.type = FinishType;
    return widget;
}

// 自定义比较函数，用于排序
bool sortByNumberPrefix(const QFileInfo &a, const QFileInfo &b)
{
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
    QRegExp numPrefixA("(\\d+).*");
    QRegExp numPrefixB("(\\d+).*");

    if (numPrefixA.indexIn(a.fileName()) == 0 && numPrefixB.indexIn(b.fileName()) == 0) {
        // 如果文件名以数字开始，则按数字顺序排序
        return numPrefixA.cap(1).toInt() < numPrefixB.cap(1).toInt();
    }
#else
    QRegularExpression re("(\\d+).*");
    auto matchA = re.match(a.fileName());
    auto matchB = re.match(b.fileName());

    if (matchA.hasMatch() && matchB.hasMatch()) {
        // 如果文件名以数字开始，则按数字顺序排序
        return matchA.captured(1).toInt() < matchB.captured(1).toInt();
    }
#endif

    // 如果不是以数字开始，则按字典顺序排序
    return a.fileName() < b.fileName();
}

void WidgetManager::initPageType()
{
    qCDebug(app) << "Initializing page types";

    QString path("/usr/share/dde-introduction");
    // 获取 XDG_DATA_DIRS 环境变量
    QString xdgDataDirs = QProcessEnvironment::systemEnvironment().value("XDG_DATA_DIRS");

    if (xdgDataDirs.isEmpty()) {
        qCWarning(app) << "XDG_DATA_DIRS environment variable is not set, using default path";
        xdgDataDirs = "/usr/share"; // default
    }

    // 将 XDG_DATA_DIRS 按冒号分割成路径列表
    QStringList xdgDataDirsList = xdgDataDirs.split(":");

    QString appdir = QString("/%1").arg(qAppName());
    // 检查每个路径是否包含 "dde-introduction" 文件夹
    foreach (const QString &dirPath, xdgDataDirsList) {
        QDir dir(dirPath + appdir);
        if (dir.exists()) {
        qCDebug(app) << "Found dde-introduction resources in:" << dirPath;
        path = dirPath + appdir;
            break;
        }
    }

    QLocale locale;

    // 2500: Paths are no longer set based on system language
#if 0
    if (locale.language() == QLocale::Chinese ||
            locale.language() == QLocale::TraditionalChineseScript ||
            locale.language() == QLocale::Taiwan)
        path += "/zh";
    else
        path += "/en";
#else
    if (isCommunityEdition()) {
        // The Community Edition uses a separate version of the video
        path += "/deepin";
    } else {
        path += "/uos";
    }
#endif

    QDir videoDir(path);
    videoDir.setFilter(QDir::Files);
    videoDir.setSorting(QDir::Name | QDir::Reversed);

    // 2500: Add mov file support
    QStringList filters;
    filters << "*.mp4" << "*.mov";

    // 获取匹配的文件列表
    QFileInfoList files = videoDir.entryInfoList(filters);
    std::sort(files.begin(), files.end(), sortByNumberPrefix);

    // 遍历文件列表并打印文件名
    for (const QFileInfo &file : files)
        m_widgetList.videoFileList.append(QUrl(QString("file://" + file.absoluteFilePath())));
}

void WidgetManager::stopAllVideos()
{
    for (const IntroductionWidget &iw : m_widgetList.videoWidgetList) {
        if (iw.w) {
#ifndef DISABLE_VIDEO
            if (auto *video = qobject_cast<VideoWidget *>(iw.w->getModel())) {
                video->stopBeforeDestroy();
            }
#endif
        }
    }
}

void WidgetManager::setVideoMute(IntroductionWidget &introWidget, bool b)
{
    qCDebug(app) << "Setting video mute state to:" << b << "for widget type:" << introWidget.type;

    // mute last video widget
    if (videoType == introWidget.type) {
#ifndef DISABLE_VIDEO
        if (auto *lastVideo = qobject_cast<VideoWidget *>(introWidget.w->getModel())) {
            lastVideo->setMute(b);
        }
#endif
        qCDebug(app) << "Video mute state set successfully";
    }
}

/**
 * @brief Check if current system is Deepin Community Edition
 *
 * This function checks the system's pretty name through DBus interface
 * to determine if it's Deepin Community Edition.
 * e.g.: "Deepin 25"
 *       "UOS Desktop 25 Professional"
 *
 * @return true if system is Deepin Community Edition
 * @return false if not or when DBus call fails
 */
bool WidgetManager::isCommunityEdition() const
{
    qCDebug(app) << "Checking if system is Community Edition";

    QDBusInterface iface("org.freedesktop.hostname1",
                        "/org/freedesktop/hostname1",
                        "org.freedesktop.hostname1",
                        QDBusConnection::systemBus());

    if (!iface.isValid()) {
        qCWarning(app) << "Failed to connect to hostname1 DBus service";
        return false;
    }

    QVariant reply = iface.property("OperatingSystemPrettyName");
    if (!reply.isValid()) {
        qCWarning(app) << "Failed to get OS name:" << iface.lastError().message();
        return false;
    }

    // Check if OS name contains "deepin" (case insensitive)
    QString osName = reply.toString().toLower();
    qCInfo(app) << "Detected OS:" << osName;

    return osName.contains("deepin");
}
