/*
 * 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 3 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/>.
 */
#include <QPixmap>
#include <QDrag>
#include <QPainter>

#include "UBLibPathViewer.h"
#include "core/UBApplication.h"
#include "board/UBBoardController.h"

#include "core/memcheck.h"
#include "core/UBDownloadManager.h"
#include "board/UBBoardPaletteManager.h"

/**
 * \brief Constructor
 * @param parent as the parent widget
 * @param name as the object name
 */
UBLibPathViewer::UBLibPathViewer(QWidget *parent, const char *name):QGraphicsView(parent)
    , mpElems(NULL)
    , mpElemsBackup(NULL)
    , mpScene(NULL)
    , mpLayout(NULL)
    , mpContainer(NULL)
    , mpBackElem(NULL)
{
    setObjectName(name);
    setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
    setAcceptDrops(true);

    mpBackElem = new UBLibElement();
    mpBackElem->setThumbnail(QPixmap(":images/libpalette/back.png").toImage());
    mpBackElem->setDeletable(false);

    mpScene = new UBPathScene(this);
    setScene(mpScene);

    mpContainer = new QGraphicsWidget();
    mpContainer->setMinimumWidth(width() - 20);
    mpScene->addItem(mpContainer);
    mpLayout = new QGraphicsLinearLayout();
    mpContainer->setLayout(mpLayout);

    connect(mpScene, SIGNAL(mouseClick(UBChainedLibElement*)), this, SLOT(onMouseClicked(UBChainedLibElement*)));
    connect(mpScene, SIGNAL(elementsDropped(QList<QString>,UBLibElement*)), this, SLOT(onElementsDropped(QList<QString>,UBLibElement*)));
    connect(horizontalScrollBar(), SIGNAL(sliderMoved(int)), this, SLOT(onSliderMoved(int)));
}

/**
 * \brief Destructor
 */
UBLibPathViewer::~UBLibPathViewer()
{
    if(NULL != mpContainer)
    {
        delete mpContainer;
        mpContainer = NULL;
    }
    if(NULL != mpBackElem)
    {
        delete mpBackElem;
        mpBackElem = NULL;
    }
    if(NULL != mpElems)
    {
        delete mpElems;
        mpElems = NULL;
    }
    //if(NULL != mpElemsBackup)
    //{
    //    delete mpElemsBackup;
    //    mpElemsBackup = NULL;
    //}
    //if(NULL != mpLayout)
    //{
    //    delete mpLayout;
    //    mpLayout = NULL;
    //}
    if(NULL != mpScene)
    {
        delete mpScene;
        mpScene = NULL;
    }
}

/**
 * \brief Display the current path
 * @param elementsChain as the path to display
 */
void UBLibPathViewer::displayPath(UBChainedLibElement *elementsChain)
{
    if(NULL != elementsChain)
    {
        mpElems = elementsChain;
        refreshPath();
    }
}

/**
 * \brief Refresh the current path
 */
void UBLibPathViewer::refreshPath()
{
    if (mpScene && mpContainer)
        mpScene->removeItem(mpContainer);
    if(mpContainer)
        delete mpContainer;
    mVItems.clear();
    mpScene->mapWidgetToChainedElem()->clear();
    mpContainer = new QGraphicsWidget();

    mpScene->addItem(mpContainer);
    mpLayout = new QGraphicsLinearLayout();
    mpContainer->setLayout(mpLayout);
    mSceneWidth = 0;
    addItem(mpElems);
    mpLayout->addStretch();

    updateScrolls();

}

/**
 * \brief Handle the slider moved event
 * @param value as the current slider position
 */
void UBLibPathViewer::onSliderMoved(int value)
{
    Q_UNUSED(value);
}

/**
 * \brief Update the scroll bar status
 */
void UBLibPathViewer::updateScrolls()
{
    int iLimit = mSceneWidth + 40; // 2x 20 pixels margin
    int iVp = viewport()->width();

    if(iLimit >= iVp)
    {
        int iDiff = iLimit - iVp;
        horizontalScrollBar()->setRange(0, iDiff);
    }
    else
    {
        horizontalScrollBar()->setRange(0, 0);
    }
}

/**
 * \brief Append an item to the path
 * @param elem as the element to add to the path
 */
void UBLibPathViewer::addItem(UBChainedLibElement *elem)
{
    if(NULL != elem)
    {
        // Add the icon
        QLabel* pIconLabel = new QLabel();
        pIconLabel->setStyleSheet(QString("background-color: transparent;"));
        pIconLabel->setPixmap((QPixmap::fromImage(*elem->element()->thumbnail())).scaledToWidth(PATHITEMWIDTH));
        UBFolderPath* iconWidget = reinterpret_cast<UBFolderPath*>(mpScene->addWidget(pIconLabel));
        //iconWidget->setToolTip(elem->element()->name());
        iconWidget->setWindowFlags(Qt::BypassGraphicsProxyWidget);
        mpLayout->addItem(iconWidget);
        mVItems << iconWidget;
        mpScene->mapWidgetToChainedElem()->insert(iconWidget,elem);
        mSceneWidth += pIconLabel->pixmap()->width() + 4; // 2px border

        if(NULL != elem->nextElement())
        {
            // Add the arrow
            QLabel* pArrowLabel = new QLabel();
            pArrowLabel->setStyleSheet(QString("background-color: transparent;"));
            pArrowLabel->setPixmap(QPixmap(":images/navig_arrow.png"));
            QGraphicsWidget* arrowWidget = mpScene->addWidget(pArrowLabel);
            mpLayout->addItem(arrowWidget);
            mVItems << arrowWidget;
            mSceneWidth += pArrowLabel->pixmap()->width() + 4; // 2px border

            // Recursively call this method while a next item exists
            addItem(elem->nextElement());
        }
    }
}

/**
 * \brief Handles the resize event
 * @param event as the resize event
 */
void UBLibPathViewer::resizeEvent(QResizeEvent *event)
{
 
    if(event->oldSize() == event->size())
        event->ignore();
    else{
        if(NULL != mpContainer)
            mpContainer->setMinimumWidth(width() - 20);
        
        viewport()->resize(width() - 10, viewport()->height());

        updateScrolls();
        event->accept();
    }
}

void UBLibPathViewer::showEvent(QShowEvent *event)
{
    Q_UNUSED(event);
    updateScrolls();
}

/**
 * \brief Handles the mouse move event
 * @param event as the mouse move event
 */
void UBLibPathViewer::mouseMoveEvent(QMouseEvent *event)
{
    event->ignore();
}

void UBLibPathViewer::onMouseClicked(UBChainedLibElement *elem)
{
    emit mouseClick(elem);
}

int UBLibPathViewer::widgetAt(QPointF p)
{
    int position = -1;

    for(int i = 0; i < mVItems.size(); i++)
    {
        QGraphicsWidget* pCrntWidget = mVItems.at(i);
        if(NULL != pCrntWidget)
        {
            QRectF r = pCrntWidget->rect();
            QPointF wPos = pCrntWidget->scenePos();
            int xMin = wPos.x() + r.x();
            int xMax = wPos.x() + r.x() + r.width();
            int yMin = wPos.y() + r.y();
            int yMax = wPos.y() + r.y() + r.height();

            if(p.x() >= xMin &&
               p.x() <= xMax &&
               p.y() >= yMin &&
               p.y() <= yMax)
            {
                return i;
            }
        }
    }

    return position;
}

void UBLibPathViewer::onElementsDropped(QList<QString> elements, UBLibElement *target)
{
    emit elementsDropped(elements, target);
}

void UBLibPathViewer::showBack()
{
    // Backup the current path so we can go back by clicking on the back button
    mpElemsBackup = mpElems;

    // Set the correct path to the backElem
    UBChainedLibElement* pLastElem = mpElemsBackup->lastElement();

    if(NULL != pLastElem)
    {
        mpBackElem->setPath(pLastElem->element()->path());
        mpBackElem->setType(eUBLibElementType_Folder);
        mpBackElem->setName(pLastElem->element()->name());
    }

    // Display the 'back' element
    displayPath(new UBChainedLibElement(mpBackElem));
}

UBFolderPath::UBFolderPath():QGraphicsProxyWidget()
{

}

UBFolderPath::~UBFolderPath()
{

}

/**
 * \brief Handles the drag enter event
 * @param pEvent as the drag enter event
 */
void UBFolderPath::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    event->acceptProposedAction();
}

/**
 * \brief Handles the drop event
 * @param pEvent as the drop event
 */
void UBFolderPath::dropEvent(QDropEvent *pEvent)
{
    processMimeData(pEvent->mimeData());
    pEvent->acceptProposedAction();
}

/**
 * \brief Handles the drag move event
 * @param pEvent as the drag move event
 */
void UBFolderPath::dragMoveEvent(QDragMoveEvent* pEvent)
{
    pEvent->acceptProposedAction();
}

/**
 * \brief Process the given MIME data
 * @param pData as the MIME data to process
 */
void UBFolderPath::processMimeData(const QMimeData *pData)
{
    Q_UNUSED(pData);
}

/**
 * \brief Handles the mouse press event
 * @param event as the mouse press event
 */
void UBFolderPath::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
}

/**
 * \brief Handles the mouse move event
 * @param event as the mouse move event
 */
void UBFolderPath::mouseMoveEvent(QMouseEvent *event)
{
    Q_UNUSED(event);
}

/**
 * \brief Handles the mouse release event
 * @param event as the mouse release event
 */
void UBFolderPath::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);
}


UBPathScene::UBPathScene(QWidget* parent):QGraphicsScene(parent)
{
    connect(UBDownloadManager::downloadManager(), SIGNAL(allDownloadsFinished()), this, SLOT(onAllDownloadsFinished()));
}

UBPathScene::~UBPathScene()
{

}


void UBPathScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        mDragStartPos = event->scenePos();
        mClickTime = QTime::currentTime();
    }
}

/**
 * \brief Handles the mouse release event
 * @param event as the mouse release event
 */
void UBPathScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    int elapsedTimeSincePress = mClickTime.elapsed();

    if(elapsedTimeSincePress < STARTDRAGTIME)
    {
        QGraphicsWidget* pGWidget = dynamic_cast<QGraphicsWidget*>(itemAt(event->pos()));
        if(NULL != pGWidget)
        {
            // We have only one view at a time
            UBLibPathViewer* pView = dynamic_cast<UBLibPathViewer*>(this->views().at(0));
            if(NULL != pView)
            {
                int iClickedItem = pView->widgetAt(event->scenePos());
                if(-1 != iClickedItem)
                {
                    QGraphicsWidget* pFolderW = dynamic_cast<QGraphicsWidget*>(pGWidget->layout()->itemAt(iClickedItem));
                    if(NULL != pFolderW)
                    {
                        UBChainedLibElement* chElem = mMapWidgetToChainedElem[pFolderW];
                        if(NULL != chElem)
                        {
                            emit mouseClick(chElem);
                        }
                    }
                }
            }
        }
    }
}

/**
 * \brief Handles the mouse move event
 * @param event as the mouse move event
 */
void UBPathScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(event->button() == Qt::LeftButton)
    {
        if((event->pos() - mDragStartPos).manhattanLength() < QApplication::startDragDistance())
        {
            // The user is not doing a drag
            return;
        }

        // The user is performing a drag operation
        QDrag* drag = new QDrag(event->widget());
        QMimeData* mimeData = new QMimeData();
        drag->setMimeData(mimeData);
        drag->start();
    }
}


void UBPathScene::dragEnterEvent(QGraphicsSceneDragDropEvent *event)
{
    event->accept();
}

void UBPathScene::dragMoveEvent(QGraphicsSceneDragDropEvent *event)
{
    event->accept();
}

void UBPathScene::dropEvent(QGraphicsSceneDragDropEvent *event)
{
    bool bAccept = false;
    const QMimeData* pMimeData = event->mimeData();

    if(NULL != event->source() && 0 == QString::compare(event->source()->metaObject()->className(), "UBLibraryWidget")){
        UBLibElement* pTargetElement = elementFromPos(event->scenePos());
        if(NULL != pTargetElement){
            if(eUBLibElementType_Folder == pTargetElement->type()){
                // The drag comes from this application, we have now to get the list of UBLibElements*
                QList<QString> qlDroppedElems;

                foreach(QUrl url, pMimeData->urls())
                    qlDroppedElems << url.toString();

                if(!qlDroppedElems.empty()){
                    // Send a signal with the target dir and the list of ublibelement*
                    emit elementsDropped(qlDroppedElems, pTargetElement);
                }
            }
        }

        bAccept = true;
    }else if(NULL != pMimeData && pMimeData->hasUrls()){
        QList<QUrl> urls = pMimeData->urls();
        foreach(QUrl eachUrl, urls){
            QString sUrl = eachUrl.toString();
            if(!sUrl.startsWith("uniboardTool://") && !sUrl.startsWith("file://") && !sUrl.startsWith("/")){
                // The dropped URL comes from the web
                // Show the download palette if it is hidden
                UBApplication::boardController->paletteManager()->startDownloads();

                // Add the dropped url to the download list
                sDownloadFileDesc desc;
                desc.currentSize = 0;
                desc.id = 0;
                desc.isBackground = false;
                desc.modal = false;
                desc.name = QFileInfo(sUrl).fileName();
                desc.totalSize = 0;
                desc.url = sUrl;
                UBDownloadManager::downloadManager()->addFileToDownload(desc);
				bAccept = true;
            }
        }
    }
	if(!bAccept && NULL != pMimeData && pMimeData->hasText()){
        //  The user can only drop an Url in this location so if the text is not an Url,
        //  we discard it.
        QString qsTxt = pMimeData->text().remove(QRegExp("[\\0]"));
        if(qsTxt.startsWith("http")){
            // Show the download palette if it is hidden
            UBApplication::boardController->paletteManager()->startDownloads();

            // Add the dropped url to the download list
            sDownloadFileDesc desc;
            desc.currentSize = 0;
            desc.id = 0;
            desc.isBackground = false;
            desc.modal = false;
            desc.name = QFileInfo(qsTxt).fileName();
            desc.totalSize = 0;
            desc.url = qsTxt;
            UBDownloadManager::downloadManager()->addFileToDownload(desc);
            bAccept = true;
        }
    }
	if(!bAccept && NULL != pMimeData && pMimeData->hasHtml()){
        QString html = pMimeData->html();
        QString url = UBApplication::urlFromHtml(html);
        if("" != url)
        {
            // Show the download palette if it is hidden
            UBApplication::boardController->paletteManager()->startDownloads();

            // Add the dropped url to the download list
            sDownloadFileDesc desc;
            desc.currentSize = 0;
            desc.id = 0;
            desc.isBackground = false;
            desc.modal = false;
            desc.name = QFileInfo(url).fileName();
            desc.totalSize = 0;
            desc.url = url;
            UBDownloadManager::downloadManager()->addFileToDownload(desc);
			bAccept = true;
        }
    }
    if(bAccept){
        event->accept();
    }
    else{
        event->ignore();
    }
}

/**
 * \brief Return the element related to the given position
 * @param p as the given position
 *
 */
UBLibElement* UBPathScene::elementFromPos(QPointF p)
{
    UBLibElement* pElem = NULL;

    QGraphicsWidget* pGWidget = dynamic_cast<QGraphicsWidget*>(itemAt(p));
    if(NULL != pGWidget)
    {
        UBChainedLibElement* chElem = mMapWidgetToChainedElem[pGWidget];
        if(NULL != chElem)
        {
            return chElem->element();
        }
    }

    return pElem;
}

void UBPathScene::onAllDownloadsFinished()
{
    // Hide the download tab
    UBApplication::boardController->paletteManager()->stopDownloads();
}