/*
 * Copyright (C) 2017 ~ 2018 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 <DApplication>
#include <DGuiApplicationHelper>
#include <DLog>
#include <DPlatformWindowHandle>
#include <DWidget>
#include <DWidgetUtil>
#include <DWindowManagerHelper>
#include <DStandardPaths>

#include <QDir>
#include <QDBusInterface>
#include "ddlog.h"

#include "mainwindow.h"
#include "environments.h"

#ifndef DISABLE_VIDEO
#include <compositing_manager.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

DWIDGET_USE_NAMESPACE

/**
 * @brief Ensures single instance of application by file locking
 *
 * This function creates a lock file to prevent multiple instances of the application.
 * For root user, uses /tmp/dde-introduction/single as lock file.
 * For normal users, uses standard config location (typically ~/.config/dde-introduction/single).
 *
 * @return bool - true if this is the only instance, false if another instance is running
 *
 * @note Function origin: deepin-movie-reborn
 */
bool runSingleInstance()
{
    qCDebug(app) << "Enter runSingleInstance";
    std::string path;
    QString userName = QDir::homePath().section("/", -1, -1);
    if (userName == "root") {
        path = "/tmp/dde-introduction";
    } else {
        path = DStandardPaths::writableLocation(QStandardPaths::AppConfigLocation).toStdString();
    }
    QDir tdir(path.c_str());
    if (!tdir.exists()) {
        tdir.mkpath(path.c_str());
    }

    path += "/single";
    int fd = open(path.c_str(), O_WRONLY | O_CREAT, 0644);
    int flock = lockf(fd, F_TLOCK, 0);

    if (fd == -1) {
        qCWarning(app) << "Failed to open lock file:" << strerror(errno);
        return false;
    }
    if (flock == -1) {
        qCWarning(app) << "Failed to lock file:" << strerror(errno);
        return false;
    }
    return true;
}

int main(int argc, char *argv[])
{
    using namespace Dtk::Core;

    if (!QString(qgetenv("XDG_CURRENT_DESKTOP")).toLower().startsWith("deepin")) {
        setenv("XDG_CURRENT_DESKTOP", "Deepin", 1);
    }

    /* 平台支持播放时才对播放视频做相关设置 */
    #ifndef DISABLE_VIDEO
    /* 规避兼容103x环境下编译构建失败的问题，因为103x环境无dmr::utils::first_check_wayland_env()接口 */
    #if (DTK_VERSION > DTK_VERSION_CHECK(5, 4, 1, 0))
    /* 适配wayland系统环境 */
    if (dmr::utils::first_check_wayland_env()) {
        setenv("PULSE_PROP_media.role", "video", 1);
#ifndef __x86_64__
        QSurfaceFormat format;
        format.setRenderableType(QSurfaceFormat::OpenGLES);
        format.setDefaultFormat(format);
#endif
    }
    #endif
    #endif

    QDateTime current = QDateTime::currentDateTime();
    qCDebug(app) << "Application initialization started at"
             << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);
    qint64 initializeAppStartMs = current.toMSecsSinceEpoch();

    qCDebug(app) << "Setting application attributes";
    QGuiApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
    DApplication *a = nullptr;

    #if (DTK_VERSION < DTK_VERSION_CHECK(5, 4, 0, 0))
        a = new DApplication(argc, argv);
    #else
        a = DApplication::globalApplication(argc, argv);
    #endif

    a->setAttribute(Qt::AA_UseHighDpiPixmaps);
    a->setOrganizationName("deepin");
    a->setApplicationName("dde-introduction");
    a->setAttribute(Qt::AA_ForceRasterWidgets, false);

    // dapplication default setting is true

    qCDebug(app) << "Checking for existing instances";
    bool isSingleInstance = runSingleInstance();
    qCDebug(app) << "Single instance check result:" << isSingleInstance;
    if (!isSingleInstance) {
        qCCritical(app) << "Another instance is already running:" << a->applicationName();
        return -1;
    }

    a->setAutoActivateWindows(true);
    qCDebug(app) << "Auto activate windows enabled";
    a->setOrganizationName("deepin");
    a->setApplicationVersion(VERSION);
    //因为快速启动turbo原因，loadTranslator函数需要放在所有QObject::tr之前，setApplicationName之后
    qCDebug(app) << "Loading translations";
    a->loadTranslator();
    a->setApplicationDisplayName(QObject::tr("Welcome"));

    // set log format and register console and file appenders
    const QString logFormat = "%{time}{yy-MM-ddTHH:mm:ss.zzz} [%{type:-7}] [%{category}] <%{function}:%{line}> %{message}";
    Dtk::Core::DLogManager::setLogFormat(logFormat);
    /* 必须在setOrganizationName("deepin")之后设置才生效 */
    Dtk::Core::DLogManager::registerConsoleAppender();
    Dtk::Core::DLogManager::registerFileAppender();

    qCDebug(app) << "Initializing DBus connection";
    QDBusConnection dbus = QDBusConnection::sessionBus();
    dbus.registerService("com.deepin.introduction");

    QCommandLineParser cmdParser;
    cmdParser.setApplicationDescription("dde-introduction");
    cmdParser.addHelpOption();
    cmdParser.addVersionOption();
    cmdParser.process(*a);

    DGuiApplicationHelper::instance()->setPaletteType(DGuiApplicationHelper::LightType);

    static const QDate buildDate = QLocale(QLocale::English).toDate(QString(__DATE__).replace("  ", " 0"), "MMM dd yyyy");
    QString t_date = buildDate.toString("MMdd");
    // Version Time
    a->setApplicationVersion(DApplication::buildVersion(t_date));
    QSettings setting("deepin", "dde-introduction");
    bool isFirst = setting.value("IsFirst", true).toBool();
    if (isFirst) {
        int maxTest = 0;
        QString comStr;
        QString filePath = "/usr/bin/gpuinfo";
        QFile file(filePath);
        if (file.exists()) {
            while (comStr.isEmpty()) {
                QProcess process;
                QStringList list;
                list << "-c" << "gpuinfo";
                process.start("gpuinfo");
                process.waitForFinished();
                process.waitForReadyRead();

                comStr = process.readAllStandardOutput();
                if (!comStr.isEmpty())
                    break;
                maxTest++;
                if (maxTest > 5)
                    break;
                QThread::msleep(500);
            }
        } else {
            while (comStr.isEmpty()) {
                QProcess process;
                QStringList list;
                list << "-c" << "lspci | grep -i vga";
                process.start("/bin/bash", list);
                process.waitForFinished();
                process.waitForReadyRead();

                comStr = process.readAllStandardOutput();
                if (!comStr.isEmpty())
                    break;
                maxTest++;
                if (maxTest > 5)
                    break;
                QThread::msleep(500);
            }

        }
        qCInfo(app) << "GPU info:" << comStr;

        if (comStr.isEmpty())
            QThread::sleep(3);
    }

    setlocale(LC_NUMERIC, "C");

    MainWindow w;
    moveToCenter(&w);
    w.show();
    qCDebug(app) << "Main window displayed";
    w.delayInitVideo();

    dbus.registerObject("/com/deepin/introduction", &w, QDBusConnection::ExportScriptableSlots);

    current = QDateTime::currentDateTime();
    qCDebug(app) << "Application initialization completed at"
             << QDateTime::currentDateTime().toString(Qt::ISODateWithMs);

    qint64 inittalizeApoFinishMs = current.toMSecsSinceEpoch();
    qint64 time = inittalizeApoFinishMs - initializeAppStartMs;
    qCInfo(app) << "Application initialization took" << time << "ms";


    return a->exec();
}
