/*
 *   Copyright (c) 2009 Chani Armitage <chani@kde.org>
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Library General Public License as
 *   published by the Free Software Foundation; either version 2, or
 *   (at your option) 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 Library General Public
 *   License along with this program; if not, write to the
 *   Free Software Foundation, Inc.,
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "mousepluginwidget.h"

#include <Plasma/Containment>

#include <KAboutData>
#include <KAboutApplicationDialog>
#include <KDebug>

#include <QApplication>
#include <QComboBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QGridLayout>

Q_DECLARE_METATYPE(KPluginInfo)

MousePluginWidget::MousePluginWidget(const QString &pluginName, const QString &trigger, QGridLayout *layoutHack, QWidget *parent)
    : QObject(parent),
      m_configDlg(0),
      m_containment(0),
      m_lastConfigLocation(trigger),
      m_tempConfigParent(QString(), KConfig::SimpleConfig)
{
    KPluginInfo::List plugins = Plasma::ContainmentActions::listContainmentActionsInfo();
    if (plugins.isEmpty()) {
        //panic!!
        QLabel *fail = new QLabel(i18n("No plugins found, check your installation."), parent);
        layoutHack->addWidget(fail, 0, 0);
        return;
    }

    //make us some widgets
    m_pluginList = new QComboBox(parent);
    m_aboutButton = new QToolButton(parent);
    m_clearButton = new QToolButton(parent);
    m_triggerButton = new MouseInputButton(parent);
    m_configButton = new QToolButton(parent);
    //m_ui.description->setText(plugin.comment());

    //plugin list
    //FIXME is there some way to share this across all the entries?
    foreach (const KPluginInfo& plugin, plugins) {
        if (plugin.property("NoDisplay").toBool()) {
            continue;
        }

        m_pluginList->addItem(KIcon(plugin.icon()), plugin.name(), QVariant::fromValue(plugin));
        if (plugin.pluginName() == pluginName) {
            m_pluginList->setCurrentIndex(m_pluginList->count() - 1);
            m_plugin = plugin;
        }
    }

    if (! m_plugin.isValid()) {
        //probably an empty string; pick the first one
        m_pluginList->setCurrentIndex(0);
        m_plugin = plugins.first();
    }

    //I can haz config?
    m_tempConfig = KConfigGroup(&m_tempConfigParent, "test");
    if (!m_plugin.property("X-Plasma-HasConfigurationInterface").toBool()) {
        m_configButton->setVisible(false);
    }

    setTrigger(trigger);

    //pretty icons for the buttons
    m_aboutButton->setIcon(KIcon("dialog-information"));
    m_triggerButton->setIcon(KIcon("input-mouse"));
    m_configButton->setIcon(KIcon("configure"));
    m_clearButton->setIcon(KIcon("list-remove"));

    //HACK
    //FIXME what's the Right Way to do this?
    int row = layoutHack->rowCount();
    layoutHack->addWidget(m_triggerButton, row, 0);
    layoutHack->addWidget(m_pluginList, row, 1);
    layoutHack->addWidget(m_configButton, row, 2);
    layoutHack->addWidget(m_aboutButton, row, 3);
    layoutHack->addWidget(m_clearButton, row, 4);

    //connect
    connect(m_pluginList, SIGNAL(currentIndexChanged(int)), this, SLOT(setPlugin(int)));
    connect(m_triggerButton, SIGNAL(triggerChanged(QString,QString)), this, SLOT(changeTrigger(QString,QString)));
    connect(m_configButton, SIGNAL(clicked()), this, SLOT(configure()));
    connect(m_clearButton, SIGNAL(clicked()), this, SLOT(clearTrigger()));
    connect(m_aboutButton, SIGNAL(clicked()), this, SLOT(showAbout()));
}

MousePluginWidget::~MousePluginWidget()
{
    delete m_pluginInstance.data();
}

void MousePluginWidget::setPlugin(int index)
{
    m_plugin = m_pluginList->itemData(index).value<KPluginInfo>();
    //clear all the old plugin's stuff
    if (m_configDlg) {
        kDebug() << "can't happen";
        m_configDlg->deleteLater();
        m_configDlg = 0;
    }
    if (m_pluginInstance) {
        delete m_pluginInstance.data();
        kDebug() << "tried to delete instance";
    }
    //reset config button
    bool hasConfig = m_plugin.property("X-Plasma-HasConfigurationInterface").toBool();
    m_configButton->setVisible(hasConfig);
    m_tempConfig.deleteGroup();
    m_lastConfigLocation.clear();
    //FIXME a stray mousewheel deleting your config would be no fun.
    //perhaps we could mark it for deletion, and then... um.. not delete it if the user goes back to
    //that plugin? but then we'd have to record which plugin the config is *for*...
    emit configChanged(m_triggerButton->trigger());
}

void MousePluginWidget::setContainment(Plasma::Containment *ctmt)
{
    //note: since the old plugin's parent is the old containment,
    //we let that containment take care of deleting it
    m_containment = ctmt;
}

void MousePluginWidget::setTrigger(const QString &trigger)
{
    m_triggerButton->setTrigger(trigger);
    updateConfig(trigger);
}

void MousePluginWidget::clearTrigger()
{
    QString oldTrigger = m_triggerButton->trigger();
    setTrigger(QString());
    emit triggerChanged(oldTrigger, QString());

    //byebye!
    m_pluginList->deleteLater();
    m_aboutButton->deleteLater();
    m_clearButton->deleteLater();
    m_triggerButton->deleteLater();
    m_configButton->deleteLater();

    deleteLater();
}

void MousePluginWidget::changeTrigger(const QString &oldTrigger, const QString& newTrigger)
{
    updateConfig(newTrigger);
    emit triggerChanged(oldTrigger, newTrigger);
}

void MousePluginWidget::updateConfig(const QString &trigger)
{
    m_configButton->setEnabled(!trigger.isEmpty());
    m_pluginList->setEnabled(!trigger.isEmpty());
}

void MousePluginWidget::configure()
{
    if (!m_pluginInstance) {
        Plasma::ContainmentActions *pluginInstance = Plasma::ContainmentActions::load(m_containment, m_plugin.pluginName());
        if (!pluginInstance) {
            //FIXME tell user
            kDebug() << "failed to load plugin!";
            return;
        }

        m_pluginInstance = pluginInstance;

        if (m_lastConfigLocation.isEmpty()) {
            pluginInstance->restore(m_tempConfig);
        } else {
            KConfigGroup cfg = m_containment->config();
            cfg = KConfigGroup(&cfg, "ActionPlugins");
            cfg = KConfigGroup(&cfg, m_lastConfigLocation);
            pluginInstance->restore(cfg);
        }
    }

    if (!m_configDlg) {
        m_configDlg = new QDialog(qobject_cast<QWidget*>(parent()));
        QLayout *lay = new QVBoxLayout(m_configDlg);
        m_configDlg->setLayout(lay);
        m_configDlg->setWindowModality(Qt::WindowModal);

        //put the config in the dialog
        QWidget *w = m_pluginInstance.data()->createConfigurationInterface(m_configDlg);
        if (w) {
            lay->addWidget(w);
        }
	const QString title = w->windowTitle();

        m_configDlg->setWindowTitle(title.isEmpty() ? i18n("Configure Plugin") :title);
        //put buttons below
        QDialogButtonBox *buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel,
                                                         Qt::Horizontal, m_configDlg);
        lay->addWidget(buttons);

        //TODO other signals?
        connect(buttons, SIGNAL(accepted()), this, SLOT(acceptConfig()));
        connect(buttons, SIGNAL(rejected()), this, SLOT(rejectConfig()));
    }

    m_configDlg->show();
}

void MousePluginWidget::acceptConfig()
{
    kDebug() << "accept";
    if (m_pluginInstance) {
        m_pluginInstance.data()->configurationAccepted();
    }

    m_configDlg->deleteLater();
    m_configDlg = 0;
    emit configChanged(m_triggerButton->trigger());
}

void MousePluginWidget::rejectConfig()
{
    kDebug() << "reject";
    m_configDlg->deleteLater();
    m_configDlg = 0;
}

void MousePluginWidget::prepareForSave()
{
    if (!m_configButton->isVisible() || m_pluginInstance || m_lastConfigLocation.isEmpty()) {
        return;
    }

    //back up our config because it'll be erased for saving
    KConfigGroup cfg = m_containment->config();
    cfg = KConfigGroup(&cfg, "ActionPlugins");
    cfg = KConfigGroup(&cfg, m_lastConfigLocation);
    cfg.copyTo(&m_tempConfig);
    //kDebug() << "copied to temp";
}

void MousePluginWidget::save()
{
    QString trigger = m_triggerButton->trigger();
    if (trigger.isEmpty()) {
        m_lastConfigLocation.clear();
        return;
    }

    if (m_pluginInstance || !m_lastConfigLocation.isEmpty()) {
        KConfigGroup cfg = m_containment->config();
        cfg = KConfigGroup(&cfg, "ActionPlugins");
        cfg = KConfigGroup(&cfg, trigger);
        if (m_pluginInstance) {
            m_pluginInstance.data()->save(cfg);
        } else {
            m_tempConfig.copyTo(&cfg);
            //kDebug() << "copied from temp";
        }
        m_lastConfigLocation = trigger;
    }
    m_containment->setContainmentActions(trigger, m_plugin.pluginName());
}

//copied from appletbrowser.cpp
//FIXME add a feature to KAboutApplicationDialog to delete the object
/* This is just a wrapper around KAboutApplicationDialog that deletes
the KAboutData object that it is associated with, when it is deleted.
This is required to free memory correctly when KAboutApplicationDialog
is called with a temporary KAboutData that is allocated on the heap.
(see the code below, in AppletBrowserWidget::infoAboutApplet())
*/
class KAboutApplicationDialog2 : public KAboutApplicationDialog
{
public:
    KAboutApplicationDialog2(KAboutData *ab, QWidget *parent = 0)
    : KAboutApplicationDialog(ab, parent), m_ab(ab) {}

    ~KAboutApplicationDialog2()
    {
        delete m_ab;
    }

private:
    KAboutData *m_ab;
};

void MousePluginWidget::showAbout()
{
    KAboutData *aboutData = new KAboutData(m_plugin.name().toUtf8(),
            m_plugin.name().toUtf8(),
            ki18n(m_plugin.name().toUtf8()),
            m_plugin.version().toUtf8(), ki18n(m_plugin.comment().toUtf8()),
            m_plugin.fullLicense().key(), ki18n(QByteArray()), ki18n(QByteArray()), m_plugin.website().toLatin1(),
            m_plugin.email().toLatin1());

    aboutData->setProgramIconName(m_plugin.icon());

    aboutData->addAuthor(ki18n(m_plugin.author().toUtf8()), ki18n(QByteArray()), m_plugin.email().toLatin1());

    KAboutApplicationDialog *aboutDialog = new KAboutApplicationDialog2(aboutData, qobject_cast<QWidget*>(parent()));
    aboutDialog->show();
}

// vim: sw=4 sts=4 et tw=100
