/***************************************************************************
 *   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 <KCmdLineArgs>
#include <KAboutData>
#include <KMenu>
#include <KService>

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

#include "skgmainpanel.h"
#include "skgtraces.h"
#include "skgdocument.h"
#include "skginterfaceplugin.h"
#include "skgservices.h"
#include "skgzoomselector.h"
#include "skgflowlayout.h"
#include "skgboardwidget.h"

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

SKGDashboardPluginWidget::SKGDashboardPluginWidget(SKGDocument* iDocument)
    : SKGTabPage(iDocument), m_flowLayout(NULL), m_menu(NULL), m_addMenu(NULL), 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 = SKGMainPanel::getMainPanel()->getPlasmaApplets();
            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>");
        }

        // 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, 0, 0, 0);
    }

    // Plug buttons with menus
    if (m_addMenu && ui.kAddWidget) {
        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_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), '-');
            SKGBoardWidget* 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(item->getZoomRation() * 5 - 15));
            }
        }
    }
    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_flowLayout ? m_flowLayout->count() : 0);
        for (int i = 0; i < nb; ++i) {
            SKGBoardWidget* item = m_itemsPointers.at(0);
            if (item) {
                m_flowLayout->removeWidget(item);
                item->hide();

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

                item->deleteLater();
            }
        }

        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 indexString = element.attribute("index");
                if (indexString.isEmpty()) indexString = '0';
                QString zoom = element.attribute("zoom");
                if (zoom.isEmpty()) zoom = '0';
                if (plugin) addItem(plugin, SKGServices::stringToInt(indexString), SKGServices::stringToInt(zoom), element.attribute("state"));
            } else {
                index = -1;
            }
            ++index;
        }

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

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

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

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

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

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

            if (iEvent->type() == QEvent::MouseButtonPress) {
                // Drag
                QMouseEvent* mevent = static_cast<QMouseEvent*>(iEvent);
                if (mevent->button() == Qt::LeftButton) {
                    m_clickedPoint = mevent->pos();
                    m_timer.stop();
                }
            } else if (iEvent->type() == QEvent::MouseButtonRelease) {
                // Drag
                QMouseEvent* mevent = static_cast<QMouseEvent*>(iEvent);
                if (mevent->button() == Qt::LeftButton) {
                    m_clickedPoint = QPoint(-1, -1);
                }
            } else if (iEvent->type() == QEvent::MouseMove) {
                // Drag
                if (m_clickedPoint != QPoint(-1, -1) && toMoveIndex != -1) {
                    QMouseEvent* mevent = static_cast<QMouseEvent*>(iEvent);
                    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 (iEvent->type() == QEvent::DragEnter) {
                // Drop move
                QDragEnterEvent* devent = static_cast<QDragEnterEvent*>(iEvent);
                if (devent->mimeData()->hasFormat("application/x-skgdashboardpluginwidget")) {
                    devent->accept();

                    return true;
                }
            } else if (iEvent->type() == QEvent::DragMove) {
                // Drop move
                QDragMoveEvent* devent = static_cast<QDragMoveEvent*>(iEvent);
                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 (iEvent->type() == QEvent::Drop) {
                // Drop
                QDropEvent* devent = static_cast<QDropEvent*>(iEvent);
                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;
                    moveItem(oldPos, toMoveIndex);

                    return true;
                }
            }
        }
    }
    return false;
}

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

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

        SKGInterfacePlugin* db = SKGMainPanel::getMainPanel()->getPluginByName(param.at(0));
        if (db) addItem(db, SKGServices::stringToInt(param.at(1)));
    }
}

void SKGDashboardPluginWidget::onMoveWidget(int iMove)
{
    // Get current position
    QWidget* send = qobject_cast<QWidget*>(this->sender());
    if (send) {
        int currentPos = m_itemsPointers.indexOf(parentBoardWidget(send));
        int newPos = currentPos + iMove;
        if (newPos < 0) {
            newPos = 0;
        } else if (newPos > m_items.count() - 1) {
            newPos = m_items.count() - 1;
        }

        moveItem(currentPos, newPos);
    }
}

void SKGDashboardPluginWidget::moveItem(int iFrom, int iTo)
{
    // Compute new position
    if (iTo != iFrom) {
        // Move item
        m_items.move(iFrom, iTo);
        m_itemsPointers.move(iFrom, iTo);

        // Build list of items in the right order
        QList<SKGBoardWidget*> listWidgets;
        int nb = m_itemsPointers.count();
        for (int i = 0; i < nb; ++i) {
            SKGBoardWidget* wgt2 = m_itemsPointers.at(i);
            m_flowLayout->removeWidget(wgt2);
            listWidgets.push_back(wgt2);
        }

        // Add items
        nb = listWidgets.count();
        for (int i = 0; i < nb; ++i) {
            SKGBoardWidget* dbw = listWidgets.at(i);
            dbw->setParent(ui.kContent);
            m_flowLayout->addWidget(dbw);
        }
    }
}

SKGBoardWidget* SKGDashboardPluginWidget::parentBoardWidget(QWidget* iWidget)
{
    SKGBoardWidget* output = qobject_cast< SKGBoardWidget* >(iWidget);
    if (!output && iWidget) {
        QWidget* iParent = iWidget->parentWidget();
        if (iParent)  output = SKGDashboardPluginWidget::parentBoardWidget(iParent);
    }

    return output;
}

void SKGDashboardPluginWidget::onRemoveWidget()
{
    int p = -1;
    QWidget* send = qobject_cast<QWidget*>(this->sender());
    if (send) p = m_itemsPointers.indexOf(parentBoardWidget(send));
    if (p >= 0) {
        // Get item
        SKGBoardWidget* wgt = m_itemsPointers.at(p);

        // Delete widget
        m_flowLayout->removeWidget(wgt);
        wgt->hide();
        wgt->deleteLater();

        // Remove item
        m_items.removeAt(p);
        m_itemsPointers.removeAt(p);
    }
}

void SKGDashboardPluginWidget::addItem(SKGInterfacePlugin* iDashboard, int iIndex, int iZoom, const QString& iState)
{
    if (iDashboard && m_flowLayout) {
        SKGBoardWidget* 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);
            }

            // Connect widget
            connect(dbw, SIGNAL(requestRemove()), this, SLOT(onRemoveWidget()), Qt::QueuedConnection);
            connect(dbw, SIGNAL(requestMove(int)), this, SLOT(onMoveWidget(int)), Qt::QueuedConnection);

            // Set size
            dbw->setZoomRation((iZoom + 15.0) / 5.0);

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

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