/***************************************************************************
 *   Copyright (C) 2008 by S. MANKOWSKI / G. DE BURE support@mankowski.fr  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>  *
 ***************************************************************************/
/** @file
 * A dashboard.
 *
 * @author Stephane MANKOWSKI
 */
#include "skgdashboardpluginwidget.h"
#include "skgmainpanel.h"
#include "skgtraces.h"
#include "skgdocument.h"
#include "skginterfaceplugin.h"
#include "skgservices.h"
#include "skgzoomselector.h"

#include <kcmdlineargs.h>
#include <kaboutdata.h>
#include <kmenu.h>
#include <KService>
#include <Plasma/DataEngine>

#include <QDomDocument>
#include <QSpacerItem>
#include <QMouseEvent>
#include <QVBoxLayout>

#define PLASMA (SKGServices::getenv("SKGPLASMA").isEmpty() ? false : true)

SKGDashboardPluginWidget::SKGDashboardPluginWidget(SKGDocument* iDocument)
    : SKGTabPage(iDocument), m_flowLayout(NULL), m_menu(NULL), m_addMenu(NULL), m_currentIndex(-1), m_plasmadashboard(NULL)
{
    SKGTRACEIN(1, "SKGDashboardPluginWidget::SKGDashboardPluginWidget");
    if (!iDocument) return;

    ui.setupUi(this);

    // Create a context menu for adding widgets
    setContextMenuPolicy(Qt::CustomContextMenu);
    m_menu = new KMenu(this);
    connect(this , SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showHeaderMenu(QPoint)));
    m_addMenu = m_menu->addMenu(KIcon("list-add"),  i18nc("Verb", "Add"));

    if (PLASMA) {
        // Load the plasma kpart
        KService::Ptr service = KService::serviceByDesktopPath("plasma-kpart.desktop");
        if (service)  m_plasmadashboard = service->createInstance<KParts::ReadOnlyPart> (this);
        if (m_plasmadashboard) {
            QVBoxLayout* l = new QVBoxLayout(ui.kContent);
            l->addWidget(m_plasmadashboard->widget());
        }

        //Build menu
        if (m_addMenu) {
            const KPluginInfo::List skglist = Plasma::Applet::listAppletInfo("", "skrooge");
            foreach(const KPluginInfo & info, skglist) {
                //Create menu
                QAction* act = m_addMenu->addAction(info.name());
                if (act) {
                    act->setIcon(KIcon(info.icon()));
                    act->setData(info.pluginName());

                    connect(act, SIGNAL(triggered(bool)), this, SLOT(onAddApplet()));
                }
            }
            connect(this, SIGNAL(appletAdded(QString)), m_plasmadashboard, SLOT(addApplet(QString)));
        }
    } else {
        //Drag and drop
        m_clickedPoint = QPoint(-1, -1);

        const KAboutData* about = KCmdLineArgs::aboutData();
        if (about) {
            ui.kTitle->setPixmap(KIcon(about->programIconName()).pixmap(22, 22), KTitleWidget::ImageLeft);
            ui.kTitle->setComment("<html><body><b>" % i18nc("Message", "Welcome to %1", about->programName()) % "</b></body></html>");
        }

        //Floating panel
        m_floatingPanel = new QWidget(this);
        m_floatingPanel->resize(200, 24);
        m_floatingPanel->setAutoFillBackground(true);
        m_floatingPanel->setBackgroundRole(QPalette::AlternateBase);

        QHBoxLayout* kHorizontalLayout = new QHBoxLayout(m_floatingPanel);
        kHorizontalLayout->setObjectName(QString::fromUtf8("kDelete"));
        kHorizontalLayout->setSpacing(0);
        kHorizontalLayout->setContentsMargins(0, 0, 0, 0);
        QToolButton* kDelete = new QToolButton(m_floatingPanel);
        kDelete->setObjectName(QString::fromUtf8("kDelete"));
        kDelete->setAutoRaise(true);
        kDelete->setIcon(KIcon("edit-delete"));
        kDelete->setToolTip(i18nc("Verb", "Remove"));
        connect(kDelete, SIGNAL(clicked(bool)), this, SLOT(onRemoveWidget()));
        kHorizontalLayout->addWidget(kDelete);

        QToolButton* kMoveBefore = new QToolButton(m_floatingPanel);
        kMoveBefore->setObjectName(QString::fromUtf8("kMoveBefore"));
        kMoveBefore->setAutoRaise(true);
        kMoveBefore->setIcon(KIcon("arrow-left"));
        kMoveBefore->setToolTip(i18nc("Verb", "Move before"));
        connect(kMoveBefore, SIGNAL(clicked(bool)), this, SLOT(onMoveBeforeWidget()));
        kHorizontalLayout->addWidget(kMoveBefore);

        QToolButton* kMoveAfter = new QToolButton(m_floatingPanel);
        kMoveAfter->setObjectName(QString::fromUtf8("kMoveAfter"));
        kMoveAfter->setAutoRaise(true);
        kMoveAfter->setIcon(KIcon("arrow-right"));
        kMoveAfter->setToolTip(i18nc("Verb", "Move after"));
        connect(kMoveAfter, SIGNAL(clicked(bool)), this, SLOT(onMoveAfterWidget()));
        kHorizontalLayout->addWidget(kMoveAfter);

        m_kZoom = new SKGZoomSelector(m_floatingPanel);
        m_kZoom->setObjectName(QString::fromUtf8("kZoom"));
        m_kZoom->setResetValue(-10);
        connect(m_kZoom, SIGNAL(changed(int)), this, SLOT(onZoomWidget(int)), Qt::QueuedConnection);

        kHorizontalLayout->addWidget(m_kZoom);
        m_floatingPanel->hide();

        //Time to show floating panel
        m_timer.setSingleShot(true);
        connect(&m_timer, SIGNAL(timeout()), this, SLOT(onShowFloatingPanel()));

        //Build menu
        if (m_addMenu) {
            m_addMenu->clear();

            int index = 0;
            while (index >= 0) {
                SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
                if (plugin) {
                    int nbdbw = plugin->getNbDashboardWidgets();
                    for (int j = 0; j < nbdbw; ++j) {
                        //Create menu
                        QAction* act = m_addMenu->addAction(plugin->getDashboardWidgetTitle(j));
                        if (act) {
                            act->setIcon(KIcon(plugin->icon()));
                            act->setData(QString(plugin->objectName() % '-' % SKGServices::intToString(j)));

                            connect(act, SIGNAL(triggered(bool)), this, SLOT(onAddWidget()));
                        }
                    }
                } else index = -2;
                ++index;
            }
        }

        //Build layout
        m_flowLayout = new SKGFlowLayout(ui.kContent);
        m_flowLayout->setSpacing(0);
    }

    //Plug buttons with menus
    ui.kAddWidget->setIcon(m_addMenu->icon());
    ui.kAddWidget->setMenu(m_addMenu);
    ui.kAddWidget->setPopupMode(QToolButton::InstantPopup);
}

SKGDashboardPluginWidget::~SKGDashboardPluginWidget()
{
    SKGTRACEIN(1, "SKGDashboardPluginWidget::~SKGDashboardPluginWidget");
    m_menu = NULL;
    m_addMenu = NULL;
    m_flowLayout = NULL;
    m_floatingPanel = NULL;
    m_kZoom = NULL;
    m_plasmadashboard = NULL;
}

QString SKGDashboardPluginWidget::getState()
{
    SKGTRACEIN(10, "SKGDashboardPluginWidget::getState");
    QDomDocument doc("SKGML");
    QDomElement root = doc.createElement("parameters");
    doc.appendChild(root);

    if (PLASMA) {
    } else {
        root.setAttribute("zoomPosition", SKGServices::intToString(zoomPosition()));

        int nb = m_items.count();
        for (int i = 0; i < nb; ++i) {
            QDomElement element = doc.createElement("ITEM-" % SKGServices::intToString(i + 1));
            root.appendChild(element);

            QStringList param = SKGServices::splitCSVLine(m_items.at(i), '-');
            SKGWidget* item = m_itemsPointers.at(i);
            if (item) {
                element.setAttribute("name", param.at(0));
                element.setAttribute("index", param.at(1));
                element.setAttribute("state", item->getState());
                element.setAttribute("zoom", SKGServices::intToString(m_itemsSizes.at(i)));
            }
        }
    }
    return doc.toString();
}

void SKGDashboardPluginWidget::setState(const QString& iState)
{
    SKGTRACEIN(10, "SKGDashboardPluginWidget::setState");

    QDomDocument doc("SKGML");
    doc.setContent(iState);
    QDomElement root = doc.documentElement();
    if (PLASMA) {
    } else {
        //Initialisation
        int nb = m_items.count();
        for (int i = 0; m_flowLayout && i < nb; ++i) {
            SKGWidget* item = m_itemsPointers.at(0);
            if (item) {
                m_flowLayout->removeWidget(item);
                item->hide();

                m_items.removeAt(0);
                m_itemsPointers.removeAt(0);
                m_itemsSizes.removeAt(0);

                delete item;
            }
        }

        m_currentIndex = -1;
        m_floatingPanel->hide();

        QString zoomPositionS = root.attribute("zoomPosition");
        if (zoomPositionS.isEmpty()) zoomPositionS = '0';
        setZoomPosition(SKGServices::stringToInt(zoomPositionS));

        int index = 1;
        while (index > 0) {
            QDomElement element = root.firstChildElement("ITEM-" % SKGServices::intToString(index));
            if (!element.isNull()) {
                SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByName(element.attribute("name"));
                QString index = element.attribute("index");
                if (index.isEmpty()) index = '0';
                QString zoom = element.attribute("zoom");
                if (zoom.isEmpty()) zoom = '0';
                if (plugin) addItem(plugin, SKGServices::stringToInt(index), SKGServices::stringToInt(zoom), element.attribute("state"));
            } else index = -1;
            ++index;
        }

        //In case of reset
        if (m_items.count() == 0 && root.attribute("zoomPosition").isEmpty()) {
            int index = 0;
            while (index >= 0) {
                SKGInterfacePlugin* plugin = SKGMainPanel::getMainPanel()->getPluginByIndex(index);
                if (plugin) {
                    int nb = plugin->getNbDashboardWidgets();
                    for (int j = 0; j < nb; ++j) {
                        addItem(plugin, j);
                    }
                } else index = -2;
                ++index;
            }
        }
    }
}

QString SKGDashboardPluginWidget::getDefaultStateAttribute()
{
    return "SKGDASHBOARD_DEFAULT_PARAMETERS";
}

void SKGDashboardPluginWidget::refresh()
{
    SKGTRACEIN(1, "SKGDashboardPluginWidget::refresh");
}

QWidget* SKGDashboardPluginWidget::zoomableWidget() const
{
    if (PLASMA) return NULL;
    return SKGTabPage::zoomableWidget();
}

bool SKGDashboardPluginWidget::eventFilter(QObject* object, QEvent* event)
{
    if (PLASMA) {
    } else {
        if (event && event->type() == QEvent::HoverLeave) {
            //Leave widget
            m_timer.stop();
            return true;
        }

        if (event && object &&
                (event->type() == QEvent::MouseButtonPress ||
                 event->type() == QEvent::MouseButtonRelease ||
                 event->type() == QEvent::MouseMove ||
                 event->type() == QEvent::DragEnter ||
                 event->type() == QEvent::DragMove ||
                 event->type() == QEvent::Drop ||
                 event->type() == QEvent::HoverMove
                )) {
            //Search SKGWidget corresponding to this widget
            SKGWidget* toMove = NULL;
            int toMoveIndex = -1;
            int nb = m_itemsPointers.count();
            for (int i = 0; toMove == NULL && i < nb; ++i) {
                SKGWidget* w = m_itemsPointers.at(i);
                if (w && w->getDragWidget() == object) {
                    toMove = w;
                    toMoveIndex = i;
                }
            }

            if (event->type() == QEvent::HoverMove) {
                //Move floating panel and start waiting time
                QHoverEvent* mevent = (QHoverEvent*) event;
                m_floatingPanel->hide();
                if (m_clickedPoint == QPoint(-1, -1) && mevent->pos().y() <= 20) {
                    m_timer.start(500);

                    //Memorize information
                    m_currentIndex = toMoveIndex;
                    m_lastPoint = mevent->pos();
                }

                return true;
            } else if (event->type() == QEvent::MouseButtonPress) {
                //Drag
                QMouseEvent* mevent = (QMouseEvent*) event;
                if (mevent->button() == Qt::LeftButton) {
                    m_clickedPoint = mevent->pos();
                    m_timer.stop();
                }
            } else if (event->type() == QEvent::MouseButtonRelease) {
                //Drag
                QMouseEvent* mevent = (QMouseEvent*) event;
                if (mevent->button() == Qt::LeftButton) {
                    m_clickedPoint = QPoint(-1, -1);
                }
            } else if (event->type() == QEvent::MouseMove) {
                //Drag
                if (m_clickedPoint != QPoint(-1, -1) && toMoveIndex != -1) {
                    QMouseEvent* mevent = (QMouseEvent*) event;
                    int distance = (mevent->pos() - m_clickedPoint).manhattanLength();
                    if (distance >= QApplication::startDragDistance()) {
                        QMimeData* mimeData = new QMimeData;
                        mimeData->setData("application/x-skgdashboardpluginwidget", SKGServices::intToString(toMoveIndex).toLatin1());

                        QDrag* drag = new QDrag(this);
                        drag->setMimeData(mimeData);
                        drag->exec(); // krazy:exclude=crashy

                        return true;
                    }
                }
            } else if (event->type() == QEvent::DragEnter) {
                //Drop move
                QDragEnterEvent* devent = (QDragEnterEvent*) event;
                if (devent->mimeData()->hasFormat("application/x-skgdashboardpluginwidget")) {
                    devent->accept();

                    return true;
                }
            } else if (event->type() == QEvent::DragMove) {
                //Drop move
                QDragMoveEvent* devent = (QDragMoveEvent*) event;
                if (devent->mimeData()->hasFormat("application/x-skgdashboardpluginwidget")) {
                    int oldPos = SKGServices::stringToInt(devent->mimeData()->data("application/x-skgdashboardpluginwidget"));
                    if (oldPos != toMoveIndex) devent->accept();
                    else devent->ignore();

                    return true;
                }
            } else if (event->type() == QEvent::Drop) {
                //Drop
                QDropEvent* devent = (QDropEvent*) event;
                if (devent->mimeData()->hasFormat("application/x-skgdashboardpluginwidget")) {
                    int oldPos = SKGServices::stringToInt(devent->mimeData()->data("application/x-skgdashboardpluginwidget"));

                    if (oldPos + 1 == toMoveIndex) ++toMoveIndex;

                    //Move item
                    if (toMoveIndex > oldPos) --toMoveIndex;
                    m_items.move(oldPos, toMoveIndex);
                    m_itemsPointers.move(oldPos, toMoveIndex);
                    m_itemsSizes.move(oldPos, toMoveIndex);

                    //Get new state
                    QString state = getState();

                    //Reset state
                    setState(state);

                    m_clickedPoint = QPoint(-1, -1);

                    return true;
                }
            }
        }
    }
    return false;
}

void SKGDashboardPluginWidget::showHeaderMenu(const QPoint& pos)
{
    //Display menu
    if (m_menu) m_menu->popup(mapToGlobal(pos));
}

void SKGDashboardPluginWidget::onShowFloatingPanel()
{
    QRect rc = m_floatingPanel->geometry();
    rc.moveCenter(this->mapFromGlobal((m_itemsPointers[m_currentIndex]->mapToGlobal(m_lastPoint))));
    m_floatingPanel->setGeometry(rc);

    if (m_kZoom) {
        QSize size = m_itemsPointers[m_currentIndex]->minimumSize();
        if (size.height() && size.width()) {
            m_kZoom->setEnabled(true);
            m_kZoom->setValue(m_itemsSizes[m_currentIndex] - 10, false);
        } else {
            m_kZoom->setEnabled(false);
        }
    }

    m_floatingPanel->show();
}

void SKGDashboardPluginWidget::onAddWidget()
{
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) {
        QString id = sender->data().toString();
        QStringList param = SKGServices::splitCSVLine(id, '-');

        SKGInterfacePlugin* db = SKGMainPanel::getMainPanel()->getPluginByName(param.at(0));
        if (db) {
            SKGWidget* dbw = db->getDashboardWidget(SKGServices::stringToInt(param.at(1)));
            if (dbw) {
                //Add
                m_items.push_back(id);
                m_itemsPointers.push_back(dbw);
                m_itemsSizes.push_back(0);

                //Get new state
                QString state = getState();

                //Remove added item
                m_items.pop_back();
                m_itemsPointers.pop_back();
                m_itemsSizes.pop_back();

                //Reset state
                setState(state);
            }
        }
    }
}

void SKGDashboardPluginWidget::onMoveAfterWidget()
{
    int pos = m_currentIndex;
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) pos = sender->data().toInt();

    if (pos >= 0 && pos < m_items.count() - 1) { //last one cannot be moved after
        //Move item
        m_items.move(pos, pos + 1);
        m_itemsPointers.move(pos, pos + 1);
        m_itemsSizes.move(pos, pos + 1);

        //Get new state
        QString state = getState();

        //Reset state
        setState(state);
    }
}

void SKGDashboardPluginWidget::onMoveBeforeWidget()
{
    int pos = m_currentIndex;
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) pos = sender->data().toInt();

    if (pos > 0 && pos <= m_items.count() - 1) { //first one cannot be moved before
        //Move item
        m_items.move(pos, pos - 1);
        m_itemsPointers.move(pos, pos - 1);
        m_itemsSizes.move(pos, pos - 1);

        //Get new state
        QString state = getState();

        //Reset state
        setState(state);
    }
}

void SKGDashboardPluginWidget::onRemoveWidget()
{
    int pos = m_currentIndex;
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) pos = sender->data().toInt();

    if (pos >= 0) {
        //Get item
        QString id = m_items.at(pos);
        SKGWidget* wgt = m_itemsPointers.at(pos);
        int s = m_itemsSizes.at(pos);

        //Remove item
        m_items.removeAt(pos);
        m_itemsPointers.removeAt(pos);
        m_itemsSizes.removeAt(pos);

        //Get new state
        QString state = getState();

        //We add removed object to be sure that it will be removed
        m_items.push_back(id);
        m_itemsPointers.push_back(wgt);
        m_itemsSizes.push_back(s);

        //Reset state
        setState(state);
    }
}
void SKGDashboardPluginWidget::onZoomWidget(int iValue)
{
    int pos = m_currentIndex;
    if (pos >= 0) {
        //Remove item
        m_itemsSizes.removeAt(pos);

        //Add it after
        m_itemsSizes.insert(pos, iValue + 10);

        //Get new state
        QString state = getState();

        //Reset state
        setState(state);
    }
}

void SKGDashboardPluginWidget::onZoomInWidget()
{
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) {
        int pos = sender->data().toInt();
        if (pos >= 0) {
            //Get item
            int s = m_itemsSizes.at(pos);

            //Remove item
            m_itemsSizes.removeAt(pos);

            //Add it after
            m_itemsSizes.insert(pos, qMin(s + 1, 20));

            //Get new state
            QString state = getState();

            //Reset state
            setState(state);
        }
    }
}

void SKGDashboardPluginWidget::onZoomOutWidget()
{
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) {
        int pos = sender->data().toInt();
        if (pos >= 0) {
            //Get item
            int s = m_itemsSizes.at(pos);

            //Remove item
            m_itemsSizes.removeAt(pos);

            //Add it after
            m_itemsSizes.insert(pos, qMax(s - 1, 0));

            //Get new state
            QString state = getState();

            //Reset state
            setState(state);
        }
    }
}

void SKGDashboardPluginWidget::addItem(SKGInterfacePlugin* iDashboard, int iIndex, int iZoom, const QString& iState)
{
    if (iDashboard && m_flowLayout) {
        SKGWidget* dbw = iDashboard->getDashboardWidget(iIndex);
        if (dbw) {
            //Add widget
            dbw->setParent(ui.kContent);
            dbw->setState(iState);
            m_flowLayout->addWidget(dbw);

            //Install filter
            QWidget* drag = dbw->getDragWidget();
            if (drag) {
                drag->installEventFilter(this);
                drag->setAcceptDrops(true);
                drag->setAttribute(Qt::WA_Hover);
            }

            //Set size
            QSize size = dbw->minimumSize();
            //double ratio=pow(10.0,((qreal) iZoom+1)/20.0);
            double ratio = ((double)iZoom) / 5.0 + 1.0;
            QSize newSize(size.width()*ratio, size.height()*ratio);
            dbw->setMinimumSize(newSize);

            QString id = iDashboard->objectName() % '-' % SKGServices::intToString(iIndex);
            m_items.push_back(id);
            m_itemsPointers.push_back(dbw);
            m_itemsSizes.push_back(iZoom);
        }
    }
}

void SKGDashboardPluginWidget::onAddApplet()
{
    QAction* sender = qobject_cast<QAction*>(this->sender());
    if (sender) {
        emit appletAdded(sender->data().toString());
    }
}
#include "skgdashboardpluginwidget.moc"
