/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QApplication>
//#include <QDebug>
#include <QIcon>
#include <QPainter>
#include <QStyle>

#include "MgQLayoutItem.h"
#include "MgQPlotScene.h"
#include "MgQSceneItem.h"

#include "MvQZoomStackWidget.h"

#include "MagPlusService.h"


int MvQZoomStackMenuStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const
{
    if (metric == PM_SmallIconSize)
        return 64;

    return QProxyStyle::pixelMetric(metric, option, widget);
}

#ifndef METVIEW_QT5
int MvQZoomStackMenuPlastiqueStyle::pixelMetric(PixelMetric metric, const QStyleOption* option, const QWidget* widget) const
{
    if (metric == PM_SmallIconSize)
        return 64;

    return QPlastiqueStyle::pixelMetric(metric, option, widget);
}
#endif

//=========================
//   MvQZoomStackLevel
//=========================

MvQZoomStackLevel::MvQZoomStackLevel()
{
}

MvQZoomStackLevel::~MvQZoomStackLevel()
{
}

void MvQZoomStackLevel::setImage(QImage& img)
{
    float w = img.width();
    float h = img.height();

    //We copy the image into a pixmap with the maximum sie of 60x48
    QPixmap pix;
    if (h * 60. / w <= 48.) {
        pix = QPixmap::fromImage(img).scaledToWidth(60, Qt::SmoothTransformation);
    }
    else {
        pix = QPixmap::fromImage(img).scaledToHeight(48, Qt::SmoothTransformation);
    }

    //Define the pixmap rectangle
    int pw  = pix.width();
    int ph  = pix.height();
    int pdy = (64 - ph) / 2;
    int pdx = (64 - pw) / 2;
    QRect pixRect(pdx, pdy, pw, -pdy);

    //We create a 60x60 pixmap with white background
    pixmap_ = QPixmap(64, 64);
    pixmap_.fill(Qt::white);

    //Paint the image pixmap into the middle
    QPainter painter(&pixmap_);
    painter.drawPixmap(pixRect, pix);

    //Render a border around it
    painter.setPen(QColor(230, 230, 230));
    painter.drawRect(0, 0, 64, 64);
}

//=========================
//   MvQZoomStackData
//=========================


MvQZoomStackData::MvQZoomStackData(QString grLayoutId) :
    grLayoutId_(grLayoutId)
{
    //Initially we do not have levels
    levelNum_      = 0;
    actLevel_      = -1;
    selectedLevel_ = -1;

    //levelPreviewWidth_=180;
    levelPreviewWidth_ = 100;
}


MvQZoomStackData::~MvQZoomStackData()
{
    clear();
}

void MvQZoomStackData::clear()
{
    foreach (MvQZoomStackLevel* level, levels_) {
        delete level;
    }
    levels_.clear();

    levelNum_      = 0;
    actLevel_      = -1;
    selectedLevel_ = -1;
}


int MvQZoomStackData::update(int levelNum, int actLevel, MgQLayoutItem* item)
{
    //Get the new number of levels
    int levelNumOri = levelNum_;
    int actLevelOri = actLevel_;

    //int selectedLevel=-1;

    levelNum_ = levelNum;
    actLevel_ = actLevel;

    if (levelNum_ > 30) {
        levelNum_ = 0;
        actLevel_ = 0;
    }

    // By default levelNum = 0 and actLevel = -1
    // and there are no zoom levels.

    if (levelNum_ <= 0) {
        clear();

        //The gui is not enabled yet
        //enabled_=false;

        //Clear the preview textures
        //clear();

        //Save the preview map into a texture
        if (levelNum_ == 0 && actLevel_ == -1) {
            if (levels_.count() == 0)  // && currentLayout_ != 0)
            {
                MvQZoomStackLevel* level = new MvQZoomStackLevel;
                generatePreview(level, item);
                //level->tb()->setChecked(true);
                levels_.push_back(level);
                selectedLevel_ = 0;
            }
            else {
                generatePreview(levels_[0], item);
                selectedLevel_ = 0;
                //levels_[0]->tb()->setChecked(true);
            }
        }
        return selectedLevel_;
    }
    else {
        //enabled_=true;
    }

    //If the levelNum or the current level has changed.
    if (levelNumOri != levelNum_ || actLevelOri != actLevel_) {
        //focusedLevel_=-1;

        //If there are less levels
        if (levelNumOri > levelNum_) {
            for (int i = levelNumOri - 1; i >= levelNum_; i--) {
                MvQZoomStackLevel* level = levels_.back();
                //tbGroup_->removeButton(level->tb());
                //layout_->removeWidget(level->tb());
                delete level;
                levels_.pop_back();
            }

            generatePreview(levels_.back(), item);
            selectedLevel_ = levels_.count() - 1;
            //levels_.back()->tb()->setChecked(true);
        }

        //If there are more levels
        else if (levelNumOri < levelNum_) {
            MvQZoomStackLevel* level = new MvQZoomStackLevel;
            generatePreview(level, item);
            //layout_->addWidget(level->tb());
            //tbGroup_->addButton(level->tb(),levels_.count());
            //level->tb()->setChecked(true);
            levels_.push_back(level);
            selectedLevel_ = levels_.count() - 1;
        }

        //If the level num is the same. If the actLevel is the last one we
        //regenerate the preview
        else {
            selectedLevel_ = actLevel_;
            if (actLevel_ == levelNum_ - 1) {
                generatePreview(levels_[actLevel_], item);
                //levels_[actLevel_]->tb()->setChecked(true);
            }
        }
    }

    return selectedLevel_;
}

void MvQZoomStackData::generatePreview(MvQZoomStackLevel* level, MgQLayoutItem* qln)
{
    if (qln == 0)
        return;

    //Get the preview layout's scene rectangle. We increase the width and
    //height a bit so that the rendered image could contain the whole frame
    QRectF sourceRect = qln->sceneBoundingRect();
    sourceRect.adjust(0., 0., 5., 5.);

    //Define the size of the device where the preview plot will be rendered
    float tw = levelPreviewWidth_;

    float w  = sourceRect.width();
    float h  = sourceRect.height();
    float r  = tw / w;
    float th = h * r;

    if (th > levelPreviewWidth_) {
        th = levelPreviewWidth_;
        r  = th / h;
        tw = w * r;
    }

    //Create the device. We double the needed size for later smooth recaling!
    QImage device = QImage(tw * 2, th * 2, QImage::Format_RGB888);

    //Set the target rect
    QRectF targetRect(0, 0, device.width(), device.height());

    //Create the painter
    QPainter* painter = new QPainter(&device);

    //Fill the target device with with colour
    painter->fillRect(0, 0, device.width(), device.height(),
                      QBrush(Qt::white));

    //Render the item into the device
    qln->scene()->render(painter, targetRect, sourceRect);

    //Rescale the device to the needed side with smooth transformation. Then mirror it horizontally.
    QImage img = device.scaledToWidth(tw, Qt::SmoothTransformation).mirrored(false, true);

    img.save("/var/tmp/dummy-user/zoom.png");


    //Now we can set the preview!
    level->setImage(img);
}

bool MvQZoomStackData::setActLevel(int level)
{
    if (level >= 0 && level < levelNum_ && level != actLevel_) {
        actLevel_      = level;
        selectedLevel_ = actLevel_;
        return true;
    }
    return false;
}

void MvQZoomStackData::stepUp()
{
    slotStepTo(actLevel_ - 1);
}

void MvQZoomStackData::stepDown()
{
    slotStepTo(actLevel_ + 1);
}

void MvQZoomStackData::slotStepTo(int level)
{
    if (setActLevel(level)) {
        emit actLevelChanged(grLayoutId_, actLevel_);
    }
}

//=========================
//   MvQZoomStackWidget
//=========================

MvQZoomStackWidget::MvQZoomStackWidget(MgQPlotScene* scene, QWidget* parent) :
    QMenu(parent)
{
    setObjectName(QString::fromUtf8("zoomStackWidget"));

    plotScene_   = scene;
    activeScene_ = 0;
    currentPage_ = 0;
    skipUpdate_  = false;
#ifndef METVIEW_QT5
    setStyle(new MvQZoomStackMenuPlastiqueStyle);
#endif
    connect(this, SIGNAL(triggered(QAction*)),
            this, SLOT(slotStepTo(QAction*)));
}

MvQZoomStackWidget::~MvQZoomStackWidget()
{
    QMapIterator<QString, MvQZoomStackData*> it(page_);
    while (it.hasNext()) {
        it.next();
        delete it.value();
    }
    page_.clear();
}

void MvQZoomStackWidget::updateData()
{
    if (skipUpdate_) {
        return;
    }
}

void MvQZoomStackWidget::reset(MgQSceneItem* activeScene, bool changeActiveScene)
{
    if (skipUpdate_) {
        return;
    }

    activeScene_ = activeScene;

    if (!changeActiveScene) {
        QStringList idLst;
        foreach (MgQSceneItem* item, plotScene_->sceneItems()) {
            MgQLayoutItem* zoomLayout = item->firstProjectorItem();
            if (zoomLayout) {
                idLst << QString::fromStdString(zoomLayout->layout().id());
            }
        }

        //Remove unused scenes from the zoomstack
        foreach (QString id, page_.keys()) {
            if (idLst.indexOf(id) == -1) {
                delete page_[id];
                page_.remove(id);
            }
        }
    }

    if (!activeScene_)
        return;

    //-----------------------------
    // Identify the active scene
    //-----------------------------

    MgQLayoutItem* zoomLayout = activeScene_->firstProjectorItem();
    if (!zoomLayout)
        return;

    int levelNum = zoomLayout->layout().zoomLevels();
    int actLevel = zoomLayout->layout().zoomCurrentLevel();
    QString id   = QString::fromStdString(zoomLayout->layout().id());

    if (changeActiveScene) {
        if (!page_.contains(id)) {
            page_[id] = new MvQZoomStackData(id);

            connect(page_[id], SIGNAL(actLevelChanged(QString, int)),
                    this, SIGNAL(actLevelChanged(QString, int)));

            //Find preview layout in the scene and remove it
            MgQPreviewLayoutItem* previewLayout = activeScene_->previewLayoutItem();
            if (!previewLayout)
                return;
            plotScene_->removeItem(previewLayout);

            //Create a new scene and add the previewLayout to it!
            QGraphicsScene* previewScene = new QGraphicsScene;
            previewScene->addItem(previewLayout);
            previewLayout->setVisible(true);

            page_[id]->update(levelNum, actLevel, previewLayout);

            delete previewScene;
        }
    }
    else {
        if (!page_.contains(id)) {
            page_[id] = new MvQZoomStackData(id);

            connect(page_[id], SIGNAL(actLevelChanged(QString, int)),
                    this, SIGNAL(actLevelChanged(QString, int)));
        }


        //Find preview layout in the scene an remove it
        MgQPreviewLayoutItem* previewLayout = activeScene_->previewLayoutItem();
        if (!previewLayout)
            return;
        plotScene_->removeItem(previewLayout);

        //Create a new scene and add the previewLayout to it!
        QGraphicsScene* previewScene = new QGraphicsScene;
        previewScene->addItem(previewLayout);
        previewLayout->setVisible(true);

        page_[id]->update(levelNum, actLevel, previewLayout);

        delete previewScene;
    }


    foreach (QAction* action, actions()) {
        removeAction(action);
        delete action;
    }

    for (int i = 0; i < page_[id]->levels().count(); i++) {
        QAction* action = new QAction(this);
        action->setIcon(page_[id]->levels().at(i)->pixmap());
        action->setData(i);
        addAction(action);
    }

    if (currentPage_ != page_[id]) {
        currentPage_ = page_[id];
    }
}

MvQZoomStackData* MvQZoomStackWidget::pageData(MgQSceneItem* item)
{
    if (!item)
        return 0;

    MgQLayoutItem* zoomLayout = item->firstProjectorItem();
    if (zoomLayout) {
        QString id = QString::fromStdString(zoomLayout->layout().id());
        if (page_.find(id) != page_.end()) {
            return page_[id];
        }
    }

    return 0;
}

void MvQZoomStackWidget::slotStepUp()
{
    if (!currentPage_)
        return;

    if (currentLevel() >= 1) {
        int level   = currentLevel() - 1;
        skipUpdate_ = true;
        slotStepTo(level);
        skipUpdate_ = false;
    }
}

void MvQZoomStackWidget::slotStepDown()
{
    if (!currentPage_)
        return;

    skipUpdate_ = true;
    if (currentLevel() >= 0 && currentLevel() < levelNum() - 1) {
        int level   = currentLevel() + 1;
        skipUpdate_ = true;
        slotStepTo(level);
        skipUpdate_ = false;
    }
}

void MvQZoomStackWidget::slotStepTo(QAction* action)
{
    if (!currentPage_)
        return;

    int level = action->data().toInt();

    //qDebug() << "level" << level;

    if (level != -1) {
        skipUpdate_ = true;
        currentPage_->slotStepTo(level);
        skipUpdate_ = false;
    }
}

void MvQZoomStackWidget::slotStepTo(int level)
{
    if (!currentPage_)
        return;

    //qDebug() << "level" << level;

    if (level != -1) {
        skipUpdate_ = true;
        currentPage_->slotStepTo(level);
        skipUpdate_ = false;
    }
}

int MvQZoomStackWidget::levelNum()
{
    if (!currentPage_)
        return 0;

    return currentPage_->levels().count();
}

int MvQZoomStackWidget::currentLevel()
{
    if (!currentPage_)
        return 0;

    return currentPage_->selectedLevel();
}
