/*
 * 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 <QtGui>
#include <QtSvg>
#include <QDrag>

#include "UBGraphicsItemDelegate.h"
#include "UBGraphicsDelegateFrame.h"
#include "UBGraphicsScene.h"
#include "UBGraphicsItemUndoCommand.h"
#include "UBGraphicsItemTransformUndoCommand.h"

#include "board/UBBoardController.h" // TODO UB 4.x clean that dependency
#include "board/UBBoardView.h" // TODO UB 4.x clean that dependency

#include "core/UBApplication.h"
#include "core/UBApplicationController.h"
#include "core/UBDisplayManager.h"
#include "core/UBSettings.h"
#include "core/UBPersistenceManager.h"

#include "document/UBDocumentProxy.h"

#include "UBGraphicsWidgetItem.h"

#include "domain/UBAbstractWidget.h"

#include "web/UBWebController.h"

#include "frameworks/UBFileSystemUtils.h"
#include "board/UBDrawingController.h"

#include "core/memcheck.h"

class UBGraphicsParaschoolEditorWidgetItem;

UBGraphicsItemDelegate::UBGraphicsItemDelegate(QGraphicsItem* pDelegated, QObject * parent, bool respectRatio, bool canRotate)
    : QObject(parent)
    , mDelegated(pDelegated)
    , mDeleteButton(NULL)
    , mDuplicateButton(NULL)
    , mMenuButton(NULL)
    , mMenu(0)
    , mLockAction(0)
    , mShowOnDisplayAction(0)
    , mGotoContentSourceAction(0)
    , mFrame(0)
    , mFrameWidth(UBSettings::settings()->objectFrameWidth)
    , mAntiScaleRatio(1.0)
    , mCanRotate(canRotate)
    , mCanDuplicate(true)
    , mRespectRatio(respectRatio)
    , mMimeData(NULL)
{
    // NOOP
}

void UBGraphicsItemDelegate::init()
{
    mFrame = new UBGraphicsDelegateFrame(this, QRectF(0, 0, 0, 0), mFrameWidth, mRespectRatio);
    mFrame->hide();
//    mFrame->setZValue(UBGraphicsScene::toolLayerStart + 1);
    UBGraphicsItem::assignZValue(mFrame, UBGraphicsScene::toolLayerStart + 1);
    mFrame->setFlag(QGraphicsItem::ItemIsSelectable, true);

    mDeleteButton = new DelegateButton(":/images/close.svg", mDelegated, mFrame);
    mButtons << mDeleteButton;
    connect(mDeleteButton, SIGNAL(clicked()), this, SLOT(remove()));
    if (canDuplicate()){
        mDuplicateButton = new DelegateButton(":/images/duplicate.svg", mDelegated, mFrame);
        connect(mDuplicateButton, SIGNAL(clicked(bool)), this, SLOT(duplicate()));
        mButtons << mDuplicateButton;
    }
    mMenuButton = new DelegateButton(":/images/menu.svg", mDelegated, mFrame);
    connect(mMenuButton, SIGNAL(clicked()), this, SLOT(showMenu()));
    mButtons << mMenuButton;

    buildButtons();

    foreach(DelegateButton* button, mButtons)
    {
        button->hide();
//        button->setZValue(UBGraphicsScene::toolLayerStart + 2);
        UBGraphicsItem::assignZValue(button, UBGraphicsScene::toolLayerStart + 2);
        button->setFlag(QGraphicsItem::ItemIsSelectable, true);
    }
}


UBGraphicsItemDelegate::~UBGraphicsItemDelegate()
{
    qDeleteAll(mButtons);
    // do not release mMimeData.
    // the mMimeData is owned by QDrag since the setMimeData call as specified in the documentation
}

QVariant UBGraphicsItemDelegate::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value)
{
    if ((change == QGraphicsItem::ItemSelectedHasChanged
         || change == QGraphicsItem::ItemPositionHasChanged
         || change == QGraphicsItem::ItemTransformHasChanged)
        && mDelegated->scene())
        {
        mAntiScaleRatio = 1 / (UBApplication::boardController->systemScaleFactor() * UBApplication::boardController->currentZoom());

        if (mDelegated->isSelected())
        {
            QList<QGraphicsItem*> items = mDelegated->scene()->selectedItems();

            foreach(QGraphicsItem* item, items)
            {
                if (item != mDelegated)
                {
                    item->setSelected(false);
                }
            }
        }

        positionHandles();
    }

    if (change == QGraphicsItem::ItemPositionHasChanged
        || change == QGraphicsItem::ItemTransformHasChanged
        || change == QGraphicsItem::ItemZValueHasChanged)
    {
        UBGraphicsScene* ubScene = qobject_cast<UBGraphicsScene*>(mDelegated->scene());
        if(ubScene)
            ubScene->setModified(true);
    }

    return value;
}


bool UBGraphicsItemDelegate::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    mDragStartPosition = event->pos();

    startUndoStep();

    if (!mDelegated->isSelected())
    {
        mDelegated->setSelected(true);

        UBGraphicsScene* scene = static_cast<UBGraphicsScene*>(mDelegated->scene());

        qDebug() << mDelegated->zValue();
        qDebug() << scene->currentObjectZIndex();

// To investigate. Z value behavior
//                if (mDelegated->zValue() < scene->currentObjectZIndex() && !isLocked())
//            mDelegated->setZValue(scene->getNextObjectZIndex());

        positionHandles();

        return true;
    }
    else
    {
        return false;
    }
}

void UBGraphicsItemDelegate::setMimeData(QMimeData *mimeData)
{
    mMimeData = mimeData;
}

bool UBGraphicsItemDelegate::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(mMimeData)
    {
        QDrag* mDrag = new QDrag(event->widget());
        mDrag->setMimeData(mMimeData);
        if (!mDragPixmap.isNull()) {
            mDrag->setPixmap(mDragPixmap);
            mDrag->setHotSpot(mDragPixmap.rect().center());
        }
        mDrag->exec();
        mDragPixmap = QPixmap();

        return true;
    }

    if(isLocked())
    {
        event->accept();
        return true;
    }
    else
    {
        return false;
    }

    
}

bool UBGraphicsItemDelegate::weelEvent(QGraphicsSceneWheelEvent *event)
{
    Q_UNUSED(event);
    if( mDelegated->isSelected() )
    {
//        event->accept();
        return true;
    }
    else
    {
//        event->ignore();
        return false;
    }
}

bool UBGraphicsItemDelegate::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    Q_UNUSED(event);

    commitUndoStep();

    return true;
}


void UBGraphicsItemDelegate::positionHandles()
{
    if (mDelegated->isSelected())
    {
        if (mFrame && !mFrame->scene() && mDelegated->scene())
        {
            mDelegated->scene()->addItem(mFrame);
        }

        mFrame->setAntiScale(mAntiScaleRatio);
        mFrame->positionHandles();
        mFrame->show();

        QTransform tr;
        tr.scale(mAntiScaleRatio, mAntiScaleRatio);

        mDeleteButton->setTransform(tr);

        qreal x = mFrame->rect().left()- mDeleteButton->renderer()->viewBox().width() * mAntiScaleRatio / 2;
        qreal y = mFrame->rect().top() - mDeleteButton->renderer()->viewBox().height() * mAntiScaleRatio / 2;

        mDeleteButton->setPos(x, y);

        if (!mDeleteButton->scene())
        {
            mDeleteButton->setParentItem(mFrame);//update parent for the case the item has been previously removed from scene
            if (mDelegated->scene())
                mDelegated->scene()->addItem(mDeleteButton);
        }

        mDeleteButton->show();

        bool shownOnDisplay = mDelegated->data(UBGraphicsItemData::ItemLayerType).toInt() != UBItemLayerType::Control;
        showHide(shownOnDisplay);

        lock(isLocked());

        for(int i = 1 ; i < mButtons.length(); i++)
        {
            DelegateButton* button = mButtons[i];

            button->setTransform(tr);
            button->setPos(x + (i * 1.6 * mFrameWidth * mAntiScaleRatio), y);
            if (!button->scene())
            {
                button->setParentItem(mFrame);//update parent for the case the item has been previously removed from scene
                if (mDelegated->scene())
                    mDelegated->scene()->addItem(button);
            }
            button->show();
        }
    }
    else
    {
        foreach(DelegateButton* button, mButtons)
            button->hide();

        mFrame->hide();
    }
}


void UBGraphicsItemDelegate::remove(bool canUndo)
{
//    QGraphicsScene* scene = mDelegated->scene();
    UBGraphicsScene* scene = (UBGraphicsScene*)(mDelegated->scene());
    if (scene)
    {
        foreach(DelegateButton* button, mButtons)
            scene->removeItem(button);

        scene->removeItem(mFrame);
        scene->removeItem(mDelegated);

        if (canUndo)
        {
            UBGraphicsItemUndoCommand *uc = new UBGraphicsItemUndoCommand((UBGraphicsScene*) scene, mDelegated, 0);
            UBApplication::undoStack->push(uc);
        }
    }
}


bool UBGraphicsItemDelegate::isLocked()
{
    return mDelegated->data(UBGraphicsItemData::ItemLocked).toBool();
}


void UBGraphicsItemDelegate::duplicate()
{
    // TODO UB 4.x .. rewrite .. .this is absurde ... we know what we are duplicating

    UBApplication::boardController->copy();
    UBApplication::boardController->paste();
}


void UBGraphicsItemDelegate::lock(bool locked)
{
    if (locked)
    {
        mDelegated->setData(UBGraphicsItemData::ItemLocked, QVariant(true));
    }
    else
    {
        mDelegated->setData(UBGraphicsItemData::ItemLocked, QVariant(false));
    }

    mDelegated->update();
    mFrame->positionHandles();
}


void UBGraphicsItemDelegate::showHide(bool show)
{
    if (show)
    {
        mDelegated->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Object));
    }
    else
    {
        mDelegated->setData(UBGraphicsItemData::ItemLayerType, QVariant(UBItemLayerType::Control));
    }

    mDelegated->update();

    emit showOnDisplayChanged(show);
}


void UBGraphicsItemDelegate::gotoContentSource(bool checked)
{
    Q_UNUSED(checked)

    UBItem* item = dynamic_cast<UBItem*>(mDelegated);

    if(item && !item->sourceUrl().isEmpty())
    {
        UBApplication::applicationController->showInternet();
        UBApplication::webController->loadUrl(item->sourceUrl());
    }
}

void UBGraphicsItemDelegate::startUndoStep()
{
    mPreviousPosition = mDelegated->pos();
    mPreviousTransform = mDelegated->transform();
    mPreviousZValue = mDelegated->zValue();
    UBResizableGraphicsItem* resizableItem = dynamic_cast<UBResizableGraphicsItem*>(mDelegated);

    if (resizableItem)
        mPreviousSize = resizableItem->size();
    else
        mPreviousSize = QSizeF();
}


void UBGraphicsItemDelegate::commitUndoStep()
{
    UBResizableGraphicsItem* resizableItem = dynamic_cast<UBResizableGraphicsItem*>(mDelegated);

    if (mDelegated->pos() != mPreviousPosition
        || mDelegated->transform() != mPreviousTransform
        || mDelegated->zValue() != mPreviousZValue
        || (resizableItem && resizableItem->size() != mPreviousSize))
    {
        UBGraphicsItemTransformUndoCommand *uc =
                new UBGraphicsItemTransformUndoCommand(mDelegated,
                                                       mPreviousPosition,
                                                       mPreviousTransform,
                                                       mPreviousZValue,
                                                       mPreviousSize);

        UBApplication::undoStack->push(uc);
    }
}


void UBGraphicsItemDelegate::decorateMenu(QMenu* menu)
{
    mLockAction = menu->addAction(tr("Locked"), this, SLOT(lock(bool)));
    QIcon lockIcon;
    lockIcon.addPixmap(QPixmap(":/images/locked.svg"), QIcon::Normal, QIcon::On);
    lockIcon.addPixmap(QPixmap(":/images/unlocked.svg"), QIcon::Normal, QIcon::Off);
    mLockAction->setIcon(lockIcon);
    mLockAction->setCheckable(true);

    mShowOnDisplayAction = mMenu->addAction(tr("Visible on Extended Screen"), this, SLOT(showHide(bool)));
    mShowOnDisplayAction->setCheckable(true);

    QIcon showIcon;
    showIcon.addPixmap(QPixmap(":/images/eyeOpened.svg"), QIcon::Normal, QIcon::On);
    showIcon.addPixmap(QPixmap(":/images/eyeClosed.svg"), QIcon::Normal, QIcon::Off);
    mShowOnDisplayAction->setIcon(showIcon);

    mGotoContentSourceAction = menu->addAction(tr("Go to Content Source"), this, SLOT(gotoContentSource(bool)));

    QIcon sourceIcon;
    sourceIcon.addPixmap(QPixmap(":/images/toolbar/internet.png"), QIcon::Normal, QIcon::On);
    mGotoContentSourceAction->setIcon(sourceIcon);

}

void UBGraphicsItemDelegate::updateMenuActionState()
{
    if (mLockAction)
        mLockAction->setChecked(isLocked());

    if (mShowOnDisplayAction)
    {
        bool isControl = mDelegated->data(UBGraphicsItemData::ItemLayerType) == UBItemLayerType::Control;
        mShowOnDisplayAction->setChecked(!isControl);
    }

    if (mGotoContentSourceAction)
    {
        UBItem* item = dynamic_cast<UBItem*>(mDelegated);
        mGotoContentSourceAction->setEnabled(item && !item->sourceUrl().isEmpty());
    }
}


void UBGraphicsItemDelegate::showMenu()
{
    if (!mMenu)
    {
        mMenu = new QMenu(UBApplication::boardController->controlView());
        decorateMenu(mMenu);
    }

    updateMenuActionState();

    UBBoardView* cv = UBApplication::boardController->controlView();
    QRect pinPos = cv->mapFromScene(mMenuButton->sceneBoundingRect()).boundingRect();

    mMenu->exec(cv->mapToGlobal(pinPos.bottomRight()));
}


