/****************************************************************************
**
** Copyright (C) 2012 - 2014 Jolla Ltd.
** Contact: http://jolla.com/
**
** This file is part of Qt Creator.
**
** 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 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Digia.
**
****************************************************************************/

#include "nxtplugin.h"
#include <steps/run/nxtresttargetrunner.h>

#include <vm/nxtaddvmstartbuildstepprojectlistener.h>
#include <steps/build/nxtbuildsteps.h>
#include <vm/nxtconnection.h>
#include <vm/nxtconnectionmanager.h>
#include <nxtconstants.h>
#include <steps/deploy/nxtdeployconfiguration.h>
#include <steps/deploy/nxtdeploysteps.h>
#include <device/nxtdevicedebugsupport.h>
#include <device/nxtdevicefactory.h>
#include <ui/options/nxtgeneraloptionspage.h>
#include <ui/mode/nxtmode.h>
#include <ui/options/nxtoptionspage.h>
#include <qtc/nxtqtversionfactory.h>
#include <steps/run/nxtrunconfigurationfactory.h>
#include <qtc/nxtsdkmanager.h>
#include <nxtsettings.h>
#include <qtc/nxttoolchainfactory.h>
#include <vm/nxtvirtualboxmanager.h>

#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/actionmanager/command.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/icore.h>
#include <coreplugin/modemanager.h>
#include <remotelinux/remotelinuxcustomrunconfiguration.h>
#include <remotelinux/remotelinuxrunconfiguration.h>
#include <utils/checkablemessagebox.h>

#include <QCheckBox>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
#include <QtPlugin>
#include <QDir>
#include <QTranslator>
#include <QLibraryInfo>

using namespace Core;
using namespace ExtensionSystem;
using namespace ProjectExplorer;
using namespace RemoteLinux;
using Utils::CheckableMessageBox;

namespace Nxt {
namespace Internal {

namespace {
const char *VM_NAME_PROPERTY = "nxtVmName";
}

NxtPlugin::NxtPlugin()
{
}

NxtPlugin::~NxtPlugin()
{
}

bool NxtPlugin::initialize(const QStringList &arguments, QString *errorString)
{
    Q_UNUSED(arguments);
    Q_UNUSED(errorString);

    using namespace ProjectExplorer::Constants;

    new NxtSettings(this);

    auto constraint = [](RunConfiguration *runConfig) {
        const Core::Id id = runConfig->id();
        return id.name().startsWith(Constants::NXT_RUNRESTCONFIGURATION_PREFIX);
    };

#if 0 /* disable standard runner */
    RunControl::registerWorker<SimpleTargetRunner>(NO_RUN_MODE, constraint);
#else
    RunControl::registerWorker<NxtRestTargetRunner>(NORMAL_RUN_MODE, constraint);
#endif /* disable standard runner */
#if defined(IDS_NXT_GDBENGINE_PATCH)
    RunControl::registerWorker<NxtDeviceDebugSupport>(DEBUG_RUN_MODE, constraint);
#else /* nxt debug disabled */
    // nil
#endif /* nxt debug disabled */
    //RunControl::registerWorker<RemoteLinuxPerfSupport>(PERFPROFILER_RUN_MODE, constraint);

    addAutoReleasedObject(new NxtSdkManager);
    addAutoReleasedObject(new NxtVirtualBoxManager);
    addAutoReleasedObject(new NxtConnectionManager);
    addAutoReleasedObject(new NxtOptionsPage);
    addAutoReleasedObject(new NxtGeneralOptionsPage);
    addAutoReleasedObject(new NxtDeviceFactory);
    addAutoReleasedObject(new NxtQtVersionFactory);
    addAutoReleasedObject(new NxtToolChainFactory);
    addAutoReleasedObject(new NxtAddVmStartBuildStepProjectListener);
    addAutoReleasedObject(new NxtDeployConfigurationFactory<NxtRestDeployConfiguration>);
    addAutoReleasedObject(new NxtRunConfigurationFactory);
    addAutoReleasedObject(new NxtBuildStepFactory<NxtSdkStartStep>);
    addAutoReleasedObject(new NxtBuildStepFactory<NxtBundleStep>);
    addAutoReleasedObject(new NxtDeployStepFactory<NxtPrepareTargetStep>);
    addAutoReleasedObject(new NxtDeployStepFactory<NxtRestDeployStep>);

#if 0 /* disable IDS NXT mode */
    addAutoReleasedObject(new NxtMode);
#endif /* disable IDS NXT mode */

    Command *sdkConnectionCommand = ActionManager::command(Constants::NXT_SDK_CONNECTON_ACTION_ID);
    ModeManager::addAction(sdkConnectionCommand->action(), 1);

    const QString locale = ICore::userInterfaceLanguage();
    if (!locale.isEmpty() && locale.length() >= 2)
    {
        QTranslator *qtr = new QTranslator(this); // do not delete
        const QString &creatorTrPath = ICore::resourcePath() + "/translations";
        const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
        const QString &trFile = "NXT_" + locale.left(2);
        if (qtr->load(trFile, qtTrPath) || qtr->load(trFile, creatorTrPath))
        {
            QCoreApplication::installTranslator(qtr);
        }
    }

    return true;
}

void NxtPlugin::extensionsInitialized()
{
}

IPlugin::ShutdownFlag NxtPlugin::aboutToShutdown()
{
    m_stopList.clear();
    QList<NxtSdk*> sdks = NxtSdkManager::sdks();
    foreach(const NxtSdk* sdk, sdks) {
        NxtConnection *connection = sdk->connection();
        bool headless = false;
        if (!connection->isVirtualMachineOff(&headless) && headless) {
            QMessageBox *prompt = new QMessageBox(
                    QMessageBox::Question,
                    tr("Close Virtual Machine"),
                    tr("The headless virtual machine \"%1\" is still running.\n\n"
                        "Close the virtual machine now?").arg(connection->virtualMachine()),
                    QMessageBox::Yes | QMessageBox::No,
                    ICore::mainWindow());
            prompt->setCheckBox(new QCheckBox(CheckableMessageBox::msgDoNotAskAgain()));
            prompt->setProperty(VM_NAME_PROPERTY, connection->virtualMachine());
            connect(prompt, &QMessageBox::finished, this, &NxtPlugin::handlePromptClosed);
            m_stopList.insert(connection->virtualMachine(), connection);
            if (NxtSettings::isAskBeforeClosingVmEnabled()) {
                prompt->open();
            } else {
                QTimer::singleShot(0, prompt, [prompt] { prompt->done(QMessageBox::Yes); });
            }
        }
    }
    if(m_stopList.isEmpty())
        return SynchronousShutdown;
    else
        return AsynchronousShutdown;
}

void NxtPlugin::handlePromptClosed(int result)
{
    QMessageBox *prompt = qobject_cast<QMessageBox *>(sender());
    prompt->deleteLater();

    QString vm = prompt->property(VM_NAME_PROPERTY).toString();

    if (prompt->checkBox() && prompt->checkBox()->isChecked())
        NxtSettings::setAskBeforeClosingVmEnabled(false);

    if (result == QMessageBox::Yes) {
        NxtConnection *connection = m_stopList.value(vm);
        connect(connection, &NxtConnection::stateChanged, this, &NxtPlugin::handleConnectionStateChanged);
        connect(connection, &NxtConnection::lockDownFailed, this, &NxtPlugin::handleLockDownFailed);
        connection->lockDown(true);
    } else {
        m_stopList.remove(vm);
    }

    if(m_stopList.isEmpty()) {
        emit asynchronousShutdownFinished();
    }
}

void NxtPlugin::handleConnectionStateChanged()
{
    NxtConnection *connection = qobject_cast<NxtConnection *>(sender());

    if (connection->state() == NxtConnection::Disconnected) {
        m_stopList.remove(connection->virtualMachine());

        if (m_stopList.isEmpty()) {
            emit asynchronousShutdownFinished();
        }
    }
}

void NxtPlugin::handleLockDownFailed()
{
    NxtConnection *connection = qobject_cast<NxtConnection *>(sender());

    m_stopList.remove(connection->virtualMachine());

    if (m_stopList.isEmpty()) {
        emit asynchronousShutdownFinished();
    }
}

} // namespace Internal
} // namespace Nxt
