/***************************************************************************
 * actioncollectionview.cpp
 * This file is part of the KDE project
 * copyright (c) 2005-2006 Cyrille Berger <cberger@cberger.net>
 * copyright (C) 2006-2007 Sebastian Sauer <mail@dipe.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 of the License, 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
 * Library General Public License for more details.
 * You should have received a copy of the GNU Library General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 ***************************************************************************/

#include "actioncollectionview.h"
#include "actioncollectionmodel.h"

#include <kross/core/manager.h>
#include <kross/core/action.h>
#include <kross/core/actioncollection.h>
#include <kross/core/interpreter.h>

#include <QtWidgets/QAction>
#include <QtWidgets/QBoxLayout>
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QLabel>
#include <QtWidgets/QLineEdit>
#include <QtWidgets/QPushButton>
#include <QtWidgets/QTreeView>

#include <kmessagebox.h>
#include <kpagedialog.h>
#include <kactioncollection.h>
#include <kcombobox.h>
#include <kiconbutton.h>
#include <klocalizedstring.h>
#include <kurlrequester.h>

//#include <ktar.h>
//#include <kio/netaccess.h>

using namespace Kross;

/*********************************************************************************
 * ActionCollectionEditor
 */

namespace Kross
{

/// \internal d-pointer class.
class ActionCollectionEditor::Private
{
public:
    enum Type { ActionType, CollectionType };
    const Type type;
    union {
        Action *action;
        ActionCollection *collection;
    };

    QString name() const
    {
        return type == ActionType ? action->name() : collection->name();
    }
    QString text() const
    {
        return type == ActionType ? action->text() : collection->text();
    }
    QString description() const
    {
        return type == ActionType ? action->description() : collection->description();
    }
    QString iconName() const
    {
        return type == ActionType ? action->iconName() : collection->iconName();
    }
    bool isEnabled() const
    {
        return type == ActionType ? action->isEnabled() : collection->isEnabled();
    }

    QLineEdit *nameedit;
    QLineEdit *textedit;
    QLineEdit *commentedit;
    QLineEdit *iconedit;
    KComboBox *interpreteredit;
    KUrlRequester *fileedit;
    //QCheckBox* enabledcheckbox;

    explicit Private(Action *a) : type(ActionType), action(a)
    {
        Q_ASSERT(a);
    }
    explicit Private(ActionCollection *c) : type(CollectionType), collection(c)
    {
        Q_ASSERT(c);
    }
};

}

ActionCollectionEditor::ActionCollectionEditor(Action *action, QWidget *parent)
    : QWidget(parent), d(new Private(action))
{
    initGui();
}

ActionCollectionEditor::ActionCollectionEditor(ActionCollection *collection, QWidget *parent)
    : QWidget(parent), d(new Private(collection))
{
    initGui();
}

ActionCollectionEditor::~ActionCollectionEditor()
{
    delete d;
}

Action *ActionCollectionEditor::action() const
{
    return d->type == Private::ActionType ? d->action : 0;
}

ActionCollection *ActionCollectionEditor::collection() const
{
    return d->type == Private::CollectionType ? d->collection : 0;
}

QLineEdit *ActionCollectionEditor::nameEdit() const
{
    return d->nameedit;
}
QLineEdit *ActionCollectionEditor::textEdit() const
{
    return d->textedit;
}
QLineEdit *ActionCollectionEditor::commentEdit() const
{
    return d->commentedit;
}
QLineEdit *ActionCollectionEditor::iconEdit() const
{
    return d->iconedit;
}
QComboBox *ActionCollectionEditor::interpreterEdit() const
{
    return d->interpreteredit;
}
KUrlRequester *ActionCollectionEditor::fileEdit() const
{
    return d->fileedit;
}

void ActionCollectionEditor::initGui()
{
    QVBoxLayout *mainlayout = new QVBoxLayout();
    setLayout(mainlayout);

    QWidget *w = new QWidget(this);
    mainlayout->addWidget(w);
    QGridLayout *gridlayout = new QGridLayout();
    gridlayout->setMargin(0);
    //gridlayout->setSpacing(0);
    w->setLayout(gridlayout);

    QLabel *namelabel = new QLabel(i18n("Name:"), w);
    gridlayout->addWidget(namelabel, 0, 0);
    d->nameedit = new QLineEdit(w);
    namelabel->setBuddy(d->nameedit);
    d->nameedit->setText(d->name());
    d->nameedit->setEnabled(false);
    gridlayout->addWidget(d->nameedit, 0, 1);

    QLabel *textlabel = new QLabel(i18n("Text:"), w);
    gridlayout->addWidget(textlabel, 1, 0);
    d->textedit = new QLineEdit(w);
    textlabel->setBuddy(d->textedit);
    d->textedit->setText(d->text());
    gridlayout->addWidget(d->textedit, 1, 1);

    QLabel *commentlabel = new QLabel(i18n("Comment:"), w);
    gridlayout->addWidget(commentlabel, 2, 0);
    d->commentedit = new QLineEdit(w);
    commentlabel->setBuddy(d->commentedit);
    d->commentedit->setText(d->description());
    gridlayout->addWidget(d->commentedit, 2, 1);

    QLabel *iconlabel = new QLabel(i18n("Icon:"), w);
    gridlayout->addWidget(iconlabel, 3, 0);
    QWidget *iconbox = new QWidget(w);
    QHBoxLayout *iconlayout = new QHBoxLayout();
    iconlayout->setMargin(0);
    iconbox->setLayout(iconlayout);
    d->iconedit = new QLineEdit(iconbox);
    iconlabel->setBuddy(d->iconedit);
    d->iconedit->setText(d->iconName());
    iconlayout->addWidget(d->iconedit, 1);
    KIconButton *iconbutton = new KIconButton(iconbox);
    iconbutton->setIcon(d->iconName());
    connect(iconbutton, SIGNAL(iconChanged(QString)), d->iconedit, SLOT(setText(QString)));
    iconlayout->addWidget(iconbutton);
    gridlayout->addWidget(iconbox, 3, 1);

    //QFrame* hr1 = new QFrame(w);
    //hr1->setFrameStyle(QFrame::HLine | QFrame::Sunken);
    //gridlayout->addWidget(hr1, 4, 0, -1, -1, Qt::AlignVCenter);

    if (d->type == Private::ActionType) {
        QLabel *interpreterlabel = new QLabel(i18n("Interpreter:"), w);
        gridlayout->addWidget(interpreterlabel, 4, 0);
        d->interpreteredit = new KComboBox(w);
        interpreterlabel->setBuddy(d->interpreteredit);
        d->interpreteredit->setMaxVisibleItems(10);
        d->interpreteredit->insertItems(0, Manager::self().interpreters());
        d->interpreteredit->setEditable(true);
        //c->lineEdit()->setText( d->action->interpreter() );
        int idx = Manager::self().interpreters().indexOf(d->action->interpreter());
        if (idx >= 0) {
            d->interpreteredit->setCurrentIndex(idx);
        } else {
            d->interpreteredit->setEditText(d->action->interpreter());
        }
        gridlayout->addWidget(d->interpreteredit, 4, 1);

        QLabel *filelabel = new QLabel(i18n("File:"), w);
        gridlayout->addWidget(filelabel, 5, 0);
        d->fileedit = new KUrlRequester(w);
        filelabel->setBuddy(d->fileedit);
        QStringList mimetypes;
        foreach (const QString &interpretername, Manager::self().interpreters()) {
            InterpreterInfo *info = Manager::self().interpreterInfo(interpretername);
            Q_ASSERT(info);
            mimetypes.append(info->mimeTypes().join(" ").trimmed());
        }
        //InterpreterInfo* info = Manager::self().interpreterInfo( Manager::self().interpreternameForFile( d->action->file() ) );
        //const QString defaultmime = info ? info->mimeTypes().join(" ").trimmed() : QString();

        d->fileedit->fileDialog()->setMimeTypeFilters(mimetypes);
        d->fileedit->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly);
        d->fileedit->setUrl(QUrl::fromLocalFile(d->action->file()));
        gridlayout->addWidget(d->fileedit, 5, 1);
    } else {
        d->interpreteredit = 0;
        d->fileedit = 0;
    }

    //d->enabledcheckbox = new QCheckBox(this);
    //d->enabledcheckbox->setText( i18n("Enabled") );
    //d->enabledcheckbox->setChecked( d->isEnabled() );
    //mainlayout->addWidget(d->enabledcheckbox);

    mainlayout->addStretch(1);
}

bool ActionCollectionEditor::isValid()
{
    //TODO check also if such a name already exist.
    return ! d->nameedit->text().isEmpty();
}

void ActionCollectionEditor::commit()
{
    switch (d->type) {
    case Private::ActionType: {
        d->action->setText(d->textedit->text());
        d->action->setDescription(d->commentedit->text());
        d->action->setIconName(d->iconedit->text());
        d->action->setInterpreter(d->interpreteredit->currentText());
        d->action->setFile(d->fileedit->url().path());
        //d->action->setEnabled( d->enabledcheckbox->isChecked() );
    } break;
    case Private::CollectionType: {
        d->collection->setText(d->textedit->text());
        d->collection->setDescription(d->commentedit->text());
        d->collection->setIconName(d->iconedit->text());
        //d->collection->setEnabled( d->enabledcheckbox->isChecked() );
    } break;
    default: break;
    }
}

/*********************************************************************************
 * ActionCollectionView
 */

namespace Kross
{

/// \internal d-pointer class.
class ActionCollectionView::Private
{
public:
    bool modified;
    KActionCollection *collection;
    QMap< QString, QPushButton * > buttons;
    explicit Private() : modified(false) {}
};

}

ActionCollectionView::ActionCollectionView(QWidget *parent)
    : QTreeView(parent)
    , d(new Private())
{
    header()->hide();
    setSelectionMode(QAbstractItemView::SingleSelection);
    setAlternatingRowColors(true);
    setRootIsDecorated(true);
    setSortingEnabled(false);
    setItemsExpandable(true);
    //setDragEnabled(true);
    //setAcceptDrops(true);
    setDropIndicatorShown(true);
    setDragDropMode(QAbstractItemView::InternalMove);

    d->collection = new KActionCollection(this);

    QAction *runaction = new QAction(QIcon::fromTheme("system-run"), i18n("Run"), this);
    runaction->setObjectName("run");
    runaction->setToolTip(i18n("Execute the selected script."));
    runaction->setEnabled(false);
    d->collection->addAction("run", runaction);
    connect(runaction, SIGNAL(triggered()), this, SLOT(slotRun()));

    QAction *stopaction = new QAction(QIcon::fromTheme("process-stop"), i18n("Stop"), this);
    stopaction->setObjectName("stop");
    stopaction->setToolTip(i18n("Stop execution of the selected script."));
    stopaction->setEnabled(false);
    d->collection->addAction("stop", stopaction);
    connect(stopaction, SIGNAL(triggered()), this, SLOT(slotStop()));

    QAction *editaction = new QAction(QIcon::fromTheme("document-properties"), i18n("Edit..."), this);
    editaction->setObjectName("edit");
    editaction->setToolTip(i18n("Edit selected script."));
    editaction->setEnabled(false);
    d->collection->addAction("edit", editaction);
    connect(editaction, SIGNAL(triggered()), this, SLOT(slotEdit()));

    QAction *addaction = new QAction(QIcon::fromTheme("list-add"), i18n("Add..."), this);
    addaction->setObjectName("add");
    addaction->setToolTip(i18n("Add a new script."));
    //addaction->setEnabled(false);
    d->collection->addAction("add", addaction);
    connect(addaction, SIGNAL(triggered()), this, SLOT(slotAdd()));

    QAction *removeaction = new QAction(QIcon::fromTheme("list-remove"), i18n("Remove"), this);
    removeaction->setObjectName("remove");
    removeaction->setToolTip(i18n("Remove selected script."));
    removeaction->setEnabled(false);
    d->collection->addAction("remove", removeaction);
    connect(removeaction, SIGNAL(triggered()), this, SLOT(slotRemove()));

    connect(this, SIGNAL(enabledChanged(QString)), this, SLOT(slotEnabledChanged(QString)));
    //expandAll();
}

ActionCollectionView::~ActionCollectionView()
{
    delete d;
}

void ActionCollectionView::setModel(QAbstractItemModel *m)
{
    QTreeView::setModel(m);
    d->modified = false;

    QItemSelectionModel *selectionmodel = new QItemSelectionModel(m, this);
    setSelectionModel(selectionmodel);

    connect(selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
            this, SLOT(slotSelectionChanged()));
    connect(m, SIGNAL(dataChanged(QModelIndex,QModelIndex)),
            this, SLOT(slotDataChanged(QModelIndex,QModelIndex)));
}

bool ActionCollectionView::isModified() const
{
    return d->modified;
}

void ActionCollectionView::setModified(bool modified)
{
    d->modified = modified;
}

KActionCollection *ActionCollectionView::actionCollection() const
{
    return d->collection;
}

QPushButton *ActionCollectionView::button(const QString &actionname) const
{
    return d->buttons.contains(actionname) ? d->buttons[actionname] : 0;
}

QItemSelection ActionCollectionView::itemSelection() const
{
    QAbstractProxyModel *proxymodel = dynamic_cast< QAbstractProxyModel * >(model());
    QItemSelection selection = selectionModel()->selection();
    return proxymodel ? proxymodel->mapSelectionToSource(selection) : selection;
}

QPushButton *ActionCollectionView::createButton(QWidget *parentWidget, const QString &actionname)
{
    QAction *action = d->collection->action(actionname);
    if (! action) {
        return 0;
    }
    //if( d->buttons.contains(actionname) ) delete d->buttons[];
    QPushButton *btn = new QPushButton(parentWidget);
    btn->setText(action->text());
    btn->setToolTip(action->toolTip());
    btn->setIcon(action->icon());
    btn->setEnabled(action->isEnabled());
    if (parentWidget && parentWidget->layout()) {
        parentWidget->layout()->addWidget(btn);
    }
    QObject::connect(btn, SIGNAL(clicked()), action, SLOT(trigger()));
    d->buttons.insert(actionname, btn);
    return btn;
}

void ActionCollectionView::slotEnabledChanged(const QString &actionname)
{
    if (d->buttons.contains(actionname)) {
        QAction *action = d->collection->action(actionname);
        d->buttons[ actionname ]->setEnabled(action ? action->isEnabled() : false);
    }
}

void ActionCollectionView::slotSelectionChanged()
{
    bool startenabled = selectionModel()->hasSelection();
    bool stopenabled = false;
    bool hasselection = selectionModel()->selectedIndexes().count() > 0;
    foreach (const QModelIndex &index, itemSelection().indexes()) {
        Action *action = ActionCollectionModel::action(index);
        if (startenabled && ! action) {
            startenabled = false;
        }
        if (! stopenabled) {
            stopenabled = (action && ! action->isFinalized());
        }
    }
    QAction *runaction = d->collection->action("run");
    if (runaction) {
        runaction->setEnabled(startenabled);
        emit enabledChanged("run");
    }
    QAction *stopaction = d->collection->action("stop");
    if (stopaction) {
        stopaction->setEnabled(stopenabled);
        emit enabledChanged("stop");
    }
    QAction *editaction = d->collection->action("edit");
    if (editaction) {
        editaction->setEnabled(hasselection);
        emit enabledChanged("edit");
    }
    QAction *removeaction = d->collection->action("remove");
    if (removeaction) {
        removeaction->setEnabled(hasselection);
        emit enabledChanged("remove");
    }
}

void ActionCollectionView::slotDataChanged(const QModelIndex &, const QModelIndex &)
{
    d->modified = true;
}

void ActionCollectionView::slotRun()
{
    if (! selectionModel()) {
        return;
    }
    QAction *stopaction = d->collection->action("stop");

    foreach (const QModelIndex &index, itemSelection().indexes()) {
        if (! index.isValid()) {
            continue;
        }
        if (stopaction) {
            stopaction->setEnabled(true);
            emit enabledChanged("stop");
        }
        Action *action = ActionCollectionModel::action(index);
        if (! action) {
            continue;
        }
        connect(action, SIGNAL(finished(Kross::Action*)), SLOT(slotSelectionChanged()));
        action->trigger();
    }
    slotSelectionChanged();
}

void ActionCollectionView::slotStop()
{
    if (! selectionModel()) {
        return;
    }
    foreach (const QModelIndex &index, itemSelection().indexes()) {
        if (! index.isValid()) {
            continue;
        }
        Action *action = ActionCollectionModel::action(index);
        if (! action) {
            continue;
        }
        //connect(action, SIGNAL(started(Kross::Action*)), SLOT(slotSelectionChanged()));
        //connect(action, SIGNAL(finished(Kross::Action*)), SLOT(slotSelectionChanged()));
        action->finalize();
    }
    slotSelectionChanged();
}

void ActionCollectionView::slotEdit()
{
    if (! selectionModel()) {
        return;
    }
    Action *action = 0;
    ActionCollection *collection = 0;
    foreach (const QModelIndex &index, itemSelection().indexes()) {
        if (! index.isValid()) {
            continue;
        }
        if (Action *a = ActionCollectionModel::action(index)) {
            action = a;
        } else if (ActionCollection *c = ActionCollectionModel::collection(index)) {
            collection = c;
        } else {
            continue;
        }
        break;
    }
    if ((! action) && (! collection)) {
        return;
    }
    KPageDialog *dialog = new KPageDialog(this);
    dialog->setWindowTitle(i18n("Edit"));
    dialog->setFaceType(KPageDialog::Plain);   //Auto Plain List Tree Tabbed
    ActionCollectionEditor *editor =
        action ? new ActionCollectionEditor(action, dialog)
        : new ActionCollectionEditor(collection, dialog);
    dialog->addPage(editor, i18nc("@title:group Script properties", "General"));
    //dialog->addPage(new QWidget(this), i18n("Security"));
    dialog->resize(QSize(580, 200).expandedTo(dialog->minimumSizeHint()));
    int result = dialog->exec();
    if (result == QDialog::Accepted /*&& dialog->result() == KDialog::Ok*/) {
        editor->commit();
    }
    dialog->deleteLater();
}

void ActionCollectionView::slotAdd()
{

//TODO
    KMessageBox::sorry(0, "TODO");

//ScriptManagerAddWizard wizard(this, collection);
//int result = wizard.exec();

#if 0
    if (! selectionModel()) {
        return;
    }
    ActionCollection *collection = 0;
    foreach (QModelIndex index, itemSelection().indexes()) {
        if (! index.isValid()) {
            continue;
        }
        if (ActionCollectionModel::action(index)) {
            //TODO propably add the item right after the current selected one?
            QModelIndex parent = index;
            while (parent.isValid() && ! collection) {
                parent = d->view->model()->parent(parent);
                collection = ActionCollectionModel::collection(parent);
            }
            if (collection) {
                break;    // job done
            }
        } else if (ActionCollection *c = ActionCollectionModel::collection(index)) {
            collection = c;
            break; // job done
        }
    }
    ScriptManagerAddWizard wizard(this, collection);
    int result = wizard.exec();
    Q_UNUSED(result);
#endif
}

void ActionCollectionView::slotRemove()
{
    if (! selectionModel()) {
        return;
    }
    KMessageBox::sorry(0, "TODO");
}

