/**************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the Qt Installer Framework.
**
** $QT_BEGIN_LICENSE:LGPL21$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 or version 3 as published by the Free
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
** following information to ensure the GNU Lesser General Public License
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** As a special exception, The Qt Company gives you certain additional
** rights. These rights are described in The Qt Company LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** $QT_END_LICENSE$
**
**************************************************************************/
#include "packagemanagerfinishedpage.h"
#include "packagemanagergui.h"
#include "packagemanagercore.h"

#include "component.h"
#include "componentmodel.h"
#include "errors.h"
#include "fileutils.h"
#include "messageboxhandler.h"
#include "packagemanagercore.h"
#include "progresscoordinator.h"
#include "performinstallationform.h"
#include "settings.h"
#include "utils.h"
#include "scriptengine.h"
#include "productkeycheck.h"

#include "kdsysinfo.h"

#include <QApplication>

#include <QString>
#include <QSettings>
#include <QtCore/QDir>
#include <QtCore/QPair>
#include <QtCore/QProcess>
#include <QtCore/QTimer>
#include <QTranslator>
#include <QDir>
#include <QDirIterator>
#include <QTextCodec>
#include <QFileInfo>
#include <QStringList>
#include <QScopedPointer>

#include <QCheckBox>
#include <QDesktopServices>
#include <QFileDialog>
#include <QGridLayout>
#include <QHBoxLayout>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QListWidget>
#include <QListWidgetItem>
#include <QMessageBox>
#include <QProgressBar>
#include <QPushButton>
#include <QRadioButton>
#include <QTextBrowser>
#include <QTreeView>
#include <QVBoxLayout>
#include <QShowEvent>
#include <QComboBox>

#ifdef Q_OS_WIN
# include <qt_windows.h>
# include <QWinTaskbarButton>
# include <QWinTaskbarProgress>

namespace  {
    // ==========================================================================
    // system shutdown
    // nSDType: 0 - Shutdown the system
    //          1 - Shutdown the system and turn off the power (if supported)
    //          2 - Shutdown the system and then restart the system
    void SystemShutdown(UINT nSDType)
    {
        HANDLE           hToken;
        TOKEN_PRIVILEGES tkp   ;

        ::OpenProcessToken(::GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY, &hToken);
        ::LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);

        tkp.PrivilegeCount          = 1                   ; // set 1 privilege
        tkp.Privileges[0].Attributes= SE_PRIVILEGE_ENABLED;

        // get the shutdown privilege for this process
        ::AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);

        switch (nSDType)
        {
            case 0: ::ExitWindowsEx(EWX_SHUTDOWN|EWX_FORCE, 0); break;
            case 1: ::ExitWindowsEx(EWX_POWEROFF|EWX_FORCE, 0); break;
            case 2: ::ExitWindowsEx(EWX_REBOOT  |EWX_FORCE, 0); break;
        }
    }
}
#endif

using namespace KDUpdater;
using namespace QInstaller;

// -- FinishedPage

/*!
    \class QInstaller::FinishedPage
    \inmodule QtInstallerFramework
    \brief The FinishedPage class completes the installation wizard.

    You can add the option to open the installed application to the page.
*/

/*!
    Constructs an installation finished page with \a core as parent.
*/
FinishedPage::FinishedPage(PackageManagerCore *core)
    : PackageManagerPage(core)
    , m_commitButton(0)
{
    setObjectName(QLatin1String("FinishedPage"));

    m_msgLabel = new QLabel(this);
    m_msgLabel->setWordWrap(true);
    m_msgLabel->setObjectName(QLatin1String("MessageLabel"));

#if defined Q_OS_OSX
    if (core->isInstaller()) {
        m_msgLabel->setAlignment(Qt::AlignCenter);

        m_statusIcon = new QLabel(this);
        m_statusIcon->setFixedSize(96,96);
        m_statusIcon->setScaledContents(true);
    }
#endif

    m_runItCheckBox = new QCheckBox(this);
    m_runItCheckBox->setObjectName(QLatin1String("RunItCheckBox"));
    m_runItCheckBox->setChecked(true);

    QVBoxLayout *layout = new QVBoxLayout(this);    
#if defined Q_OS_OSX
    if (core->isInstaller()) {
        QHBoxLayout *hblayout = new QHBoxLayout(this);
        hblayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred));
        hblayout->addWidget(m_statusIcon);
        hblayout->addItem(new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Preferred));
        layout->addLayout(hblayout);
    }
#endif
    layout->addWidget(m_msgLabel);
    layout->addWidget(m_runItCheckBox);
    setLayout(layout);

    setCommitPage(true);
}

/*!
    Initializes the page's fields based on values from fields on previous
    pages.
*/
void FinishedPage::entering()
{
#if defined Q_OS_WIN
    setColoredTitle(tr("Finishing the %1 Wizard").arg(productName()));
#elif defined Q_OS_OSX
    setColoredTitle(tr("Finishing the %1").arg(productName()));
#endif

#ifdef Q_OS_WIN
    if (packageManagerCore()->isInstaller() ||
            packageManagerCore()->isUninstaller())
        gui()->setOption(QWizard::IgnoreSubTitles, true);
#endif

    m_msgLabel->setText(tr("Click %1 to exit the %2 Wizard.")
                        .arg(gui()->defaultButtonText(QWizard::FinishButton).remove(QLatin1Char('&')))
                        .arg(productName()));

    if (m_commitButton) {
        disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked);
        m_commitButton = 0;
    }

    if (packageManagerCore()->isUpdater() || packageManagerCore()->isPackageManager()) {
#ifdef Q_OS_OSX
        gui()->setOption(QWizard::NoCancelButton, false);
#endif
        if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) {
            m_commitButton = cancel;
            cancel->setEnabled(true);
            cancel->setVisible(true);
            // we don't use the usual FinishButton so we need to connect the misused CancelButton
            connect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked);
            connect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked);
            // for the moment we don't want the rejected signal connected
            disconnect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled);

            connect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked,
                    this, &FinishedPage::cleanupChangedConnects);
        }
        setButtonText(QWizard::CommitButton, tr("Restart"));
        setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::FinishButton));
    } else {
        if (packageManagerCore()->isInstaller()) {
            gui()->setOption(QWizard::NoCancelButton, false);

            setButtonText(QWizard::FinishButton, tr("Restart now"));
            setButtonText(QWizard::CancelButton, tr("Later"));

            m_commitButton = wizard()->button(QWizard::FinishButton);
            if (QPushButton *const b = qobject_cast<QPushButton *>(m_commitButton)) {
                b->setDefault(true);               
                b->setVisible(true);
                b->setEnabled(true);
            }

            wizard()->button(QWizard::CancelButton)->setEnabled(true);
        }
        else {
        gui()->setOption(QWizard::NoCancelButton, true);
        if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton))
            cancel->setVisible(false);
        }
    }

    gui()->updateButtonLayout();

    if (m_commitButton) {
        disconnect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked);
        connect(m_commitButton, &QAbstractButton::clicked, this, &FinishedPage::handleFinishClicked);
    }

    if (packageManagerCore()->status() == PackageManagerCore::Success) {
        // Default text
        QString finishedText = packageManagerCore()->value(QLatin1String("FinishedText"));
        if (packageManagerCore()->isInstaller()) {
            const QString restartMessage1 = tr("The computer must restart to complete the installation.");
            const QString restartMessage2 = tr("Do you want to restart your computer now or later?");
#ifdef Q_OS_WIN
            finishedText = tr("Successfully installed %1.").arg(productName());
            finishedText = finishedText + QLatin1String("<br><br>") + restartMessage1 + QLatin1String("<br><br>") + restartMessage2;
#elif defined Q_OS_OSX
            int fontSize = m_msgLabel->font().pointSize();
            const QString titleMessage = QStringLiteral("<span style=\" font-size:%1pt;\"><b>").arg(fontSize + 5) + tr("The installation was successful.") + QStringLiteral("</b></span>") ;
            finishedText = finishedText + QLatin1String("<br><br>") + restartMessage1 + QLatin1String("<br>") + restartMessage2;

            QPixmap myPix(QStringLiteral(":/ok.png"));
            m_statusIcon->setPixmap(myPix);
#endif
        }

        if (!finishedText.isEmpty())
            m_msgLabel->setText(finishedText);

        if (!packageManagerCore()->isUninstaller() && !packageManagerCore()->value(scRunProgram)
            .isEmpty()) {
                m_runItCheckBox->show();
                m_runItCheckBox->setText(packageManagerCore()->value(scRunProgramDescription,
                    tr("Run %1 now.")).arg(productName()));
            return; // job done
        }
    } else {
        // TODO: how to handle this using the config.xml

#ifdef Q_OS_WIN
        setColoredTitle(tr("The %1 Wizard failed.").arg(productName()));
#elif defined Q_OS_OSX
        if (packageManagerCore()->isInstaller()) {
            int fontSize = m_msgLabel->font().pointSize();
            QString txt = QStringLiteral("<span style=\" font-size:%1pt;\"><b>").arg(fontSize + 5) + tr("The installation failed.") + QStringLiteral("</b></span>") ;
            m_msgLabel->setText(txt);

            QPixmap myPix(QStringLiteral(":/fail.png"));
            m_statusIcon->setPixmap(myPix);
        }
#endif

        if (m_commitButton)
            m_commitButton->setVisible(false);
        setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::FinishButton));
    }

    m_runItCheckBox->hide();
    m_runItCheckBox->setChecked(false);
}

/*!
    Called when end users leave the page and the PackageManagerGui:currentPageChanged()
    signal is triggered.
*/
void FinishedPage::leaving()
{
    gui()->setOption(QWizard::NoCancelButton, true);

    if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton))
        cancel->setVisible(false);
    gui()->updateButtonLayout();

    setButtonText(QWizard::CommitButton, gui()->defaultButtonText(QWizard::CommitButton));
    setButtonText(QWizard::CancelButton, gui()->defaultButtonText(QWizard::CancelButton));
}

/*!
    Performs the necessary operations when end users select the \uicontrol Finish
    button.
*/
void FinishedPage::handleFinishClicked()
{
    const QString program =
        packageManagerCore()->replaceVariables(packageManagerCore()->value(scRunProgram));

    const QStringList args = packageManagerCore()->replaceVariables(packageManagerCore()
        ->values(scRunProgramArguments));
    if (!m_runItCheckBox->isChecked() || program.isEmpty()) {
#ifdef Q_OS_WIN
        if (packageManagerCore()->isInstaller())
            SystemShutdown(2);
#elif defined Q_OS_OSX
        QStringList adjustedArguments;
        adjustedArguments << QStringLiteral("-r") << QStringLiteral("now");
        QProcess::startDetached(QStringLiteral("shutdown"), adjustedArguments);
#endif
        return;
    }

    qDebug() << "starting" << program << args;
    QProcess::startDetached(program, args);
}

/*!
    Removes changed connects from the page.
*/
void FinishedPage::cleanupChangedConnects()
{
    if (QAbstractButton *cancel = gui()->button(QWizard::CancelButton)) {
        // remove the workaround connect from entering page
        disconnect(cancel, &QAbstractButton::clicked, gui(), &PackageManagerGui::finishButtonClicked);
        disconnect(cancel, &QAbstractButton::clicked, packageManagerCore(), &PackageManagerCore::finishButtonClicked);
        connect(gui(), &QDialog::rejected, packageManagerCore(), &PackageManagerCore::setCanceled);

        disconnect(gui()->button(QWizard::CommitButton), &QAbstractButton::clicked,
                   this, &FinishedPage::cleanupChangedConnects);
    }
}
