// Copyright (C) 2016 Tim Sander <tim@krieglstein.org>
// Copyright (C) 2016 Denis Shienkov <denis.shienkov@gmail.com>
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#include "baremetaldevice.h"

#include "baremetalconstants.h"
#include "baremetaldevice.h"
#include "baremetaltr.h"
#include "debugserverproviderchooser.h"
#include "debugserverprovidermanager.h"
#include "idebugserverprovider.h"

#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/idevicefactory.h>
#include <projectexplorer/devicesupport/idevicewidget.h>

#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/variablechooser.h>
#include <utils/wizard.h>

#include <QFormLayout>
#include <QLineEdit>
#include <QWizardPage>

using namespace ProjectExplorer;
using namespace Utils;

namespace BareMetal::Internal {

class BareMetalDeviceWidget final : public IDeviceWidget
{
public:
    explicit BareMetalDeviceWidget(const IDevicePtr &deviceConfig)
        : IDeviceWidget(deviceConfig)
    {
        const auto dev = std::static_pointer_cast<const BareMetalDevice>(device());
        QTC_ASSERT(dev, return);

        const auto formLayout = new QFormLayout(this);
        formLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);

        m_debugServerProviderChooser = new DebugServerProviderChooser(true, this);
        m_debugServerProviderChooser->populate();
        m_debugServerProviderChooser->setCurrentProviderId(dev->debugServerProviderId());
        formLayout->addRow(Tr::tr("Debug server provider:"), m_debugServerProviderChooser);

        connect(m_debugServerProviderChooser, &DebugServerProviderChooser::providerChanged,
                this, &BareMetalDeviceWidget::debugServerProviderChanged);

        installMarkSettingsDirtyTriggerRecursively(this);
    }

private:
    void debugServerProviderChanged()
    {
        const auto dev = std::static_pointer_cast<BareMetalDevice>(device());
        QTC_ASSERT(dev, return);
        dev->setDebugServerProviderId(m_debugServerProviderChooser->currentProviderId());
    }

    void updateDeviceFromUi() final
    {
        debugServerProviderChanged();
    }

    DebugServerProviderChooser *m_debugServerProviderChooser = nullptr;
};


// BareMetalDevice

BareMetalDevice::BareMetalDevice()
{
    setDisplayType(Tr::tr("Bare Metal"));
    setOsType(Utils::OsTypeOther);

    m_debugServerProviderId.setSettingsKey("IDebugServerProviderId");
}

BareMetalDevice::~BareMetalDevice() = default;

QString BareMetalDevice::defaultDisplayName()
{
    return Tr::tr("Bare Metal Device");
}

QString BareMetalDevice::debugServerProviderId() const
{
    return m_debugServerProviderId();
}

void BareMetalDevice::setDebugServerProviderId(const QString &id)
{
    if (id == debugServerProviderId())
        return;
    m_debugServerProviderId.setValue(id);
}

void BareMetalDevice::unregisterDebugServerProvider(const QString &providerId) const
{
    if (providerId == debugServerProviderId())
        m_debugServerProviderId.setValue(QString());
}

void BareMetalDevice::fromMap(const Store &map)
{
    IDevice::fromMap(map);

    // Override wrong state from Creator 19.0.0, see QTCREATORBUG-34221
    setDeviceState(DeviceStateUnknown);

    if (debugServerProviderId().isEmpty()) {
        const QString name = displayName();
        if (IDebugServerProvider *provider =
                DebugServerProviderManager::findByDisplayName(name)) {
            setDebugServerProviderId(provider->id());
        }
    }
}

IDeviceWidget *BareMetalDevice::createWidget()
{
    return new BareMetalDeviceWidget(shared_from_this());
}

//  BareMetalDeviceConfigurationWizardSetupPage

class BareMetalDeviceConfigurationWizardSetupPage final : public QWizardPage
{
public:
    explicit BareMetalDeviceConfigurationWizardSetupPage(QWidget *parent)
        : QWizardPage(parent)
    {
        setTitle(Tr::tr("Set up Debug Server or Hardware Debugger"));

        m_nameLineEdit = new QLineEdit(this);

        m_providerChooser = new DebugServerProviderChooser(false, this);
        m_providerChooser->populate();

        const auto formLayout = new QFormLayout(this);
        formLayout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
        formLayout->addRow(Tr::tr("Name:"), m_nameLineEdit);
        formLayout->addRow(Tr::tr("Debug server provider:"), m_providerChooser);

        connect(m_nameLineEdit, &QLineEdit::textChanged,
                this, &BareMetalDeviceConfigurationWizardSetupPage::completeChanged);
        connect(m_providerChooser, &DebugServerProviderChooser::providerChanged,
                this, &QWizardPage::completeChanged);
    }

    void initializePage() final
    {
        m_nameLineEdit->setText(BareMetalDevice::defaultDisplayName());
    }

    bool isComplete() const final
    {
        return !configurationName().isEmpty();
    }

    QString configurationName() const { return m_nameLineEdit->text().trimmed(); }
    QString debugServerProviderId() const { return m_providerChooser->currentProviderId(); }

private:
    QLineEdit *m_nameLineEdit = nullptr;
    DebugServerProviderChooser *m_providerChooser = nullptr;
};

//  BareMetalDeviceConfigurationWizardSetupPage

class BareMetalDeviceConfigurationWizard final : public Wizard
{
public:
    BareMetalDeviceConfigurationWizard()
        : m_setupPage(new BareMetalDeviceConfigurationWizardSetupPage(this))
    {
        enum PageId { SetupPageId };

        setWindowTitle(Tr::tr("New Bare Metal Device Configuration Setup"));
        setPage(SetupPageId, m_setupPage);
        m_setupPage->setCommitPage(true);
    }

    IDevicePtr device() const
    {
        const auto dev = BareMetalDevice::create();
        dev->setupId(IDevice::ManuallyAdded, Utils::Id());
        dev->setDefaultDisplayName(m_setupPage->configurationName());
        dev->setType(Constants::BareMetalOsType);
        dev->setMachineType(IDevice::Hardware);
        dev->setDebugServerProviderId(m_setupPage->debugServerProviderId());
        dev->setDeviceState(IDevice::DeviceStateUnknown);
        return dev;
    }

private:
    BareMetalDeviceConfigurationWizardSetupPage *m_setupPage = nullptr;
};


// Factory

class BareMetalDeviceFactory final : public IDeviceFactory
{
public:
    BareMetalDeviceFactory()
        : IDeviceFactory(Constants::BareMetalOsType)
    {
        setDisplayName(Tr::tr("Bare Metal Device"));
        setCombinedIcon(":/baremetal/images/baremetaldevicesmall.png",
                        ":/baremetal/images/baremetaldevice.png");
        setConstructionFunction(&BareMetalDevice::create);
        setCreator([] {
            BareMetalDeviceConfigurationWizard wizard;
            if (wizard.exec() != QDialog::Accepted)
                return IDevice::Ptr();
            return wizard.device();
        });
    }
};

void setupBareMetalDevice()
{
    static BareMetalDeviceFactory theBareMetalDeviceFactory;
}

} // BareMetal::Internal
