/***************************** 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 <QDebug>
#include <QLabel>
#include <QStackedLayout>
#include <QToolButton>
#include <QVBoxLayout>

#include "MvQLayerDataWidget.h"

#include "Layer.h"
#include "Transformation.h"

#ifdef METVIEW_ODB_NEW
#include "MvQOdbDataWidget.h"
#endif

#include "MvQPlaceMark.h"
#include "MvQPlotView.h"

#include "MgQPlotScene.h"
#include "MgQRootItem.h"
#include "MgQSceneItem.h"

MvQLayerDataWidget::MvQLayerDataWidget(MgQPlotScene* plotScene, MvQPlotView* plotView, QWidget* parent) :
    QWidget(parent),
    plotScene_(plotScene),
    plotView_(plotView),
    sceneItem_(nullptr),
    layer_(nullptr),
    dataProbe_(nullptr)
{
    layout_ = new QVBoxLayout;
    layout_->setContentsMargins(0, 0, 0, 0);
    // layout_->setSpacing(1);
    setLayout(layout_);

    // Panels

    panelLayout_ = new QStackedLayout;
    layout_->addLayout(panelLayout_);

    auto* emptyWidget = new QWidget(this);
    auto* emptyVb = new QVBoxLayout(emptyWidget);
    auto* label = new QLabel(tr("No data is available for this layer!"), this);
    emptyVb->addWidget(label);
    emptyVb->addStretch(1);
    panelLayout_->addWidget(emptyWidget);

    QPair<PanelType, QWidget*> item(EmptyPanel, label);
    panels_ << item;

    // Data probe

    auto* probeHb = new QHBoxLayout();
    probeHb->setSpacing(1);
    probeHb->setContentsMargins(1, 1, 1, 1);
    layout_->addLayout(probeHb);

    probeTb_ = new QToolButton(this);
    probeTb_->setToolTip(tr("Enable data probe"));
    probeTb_->setIcon(QPixmap(QString::fromUtf8(":/uPlot/data_probe.svg")));
    probeTb_->setText("Probe");
    probeTb_->setCheckable(true);
    probeTb_->setChecked(false);

    resetProbeTb_ = new QToolButton(this);
    resetProbeTb_->setToolTip(tr("Reset probe"));
    resetProbeTb_->setIcon(QPixmap(QString::fromUtf8(":/uPlot/reset.svg")));

    filterProbeTb_ = new QToolButton(this);
    filterProbeTb_->setToolTip(tr("Only show results collected by the probe"));
    filterProbeTb_->setIcon(QPixmap(QString::fromUtf8(":/uPlot/filter_blue.svg")));
    filterProbeTb_->setCheckable(true);
    filterProbeTb_->setChecked(false);

    probeHb->addWidget(probeTb_);
    probeHb->addWidget(resetProbeTb_);
    probeHb->addWidget(filterProbeTb_);
    probeHb->addSpacing(24);

    probeLabel_ = new QLabel("", this);
    probeLabel_->setObjectName(QString::fromUtf8("fileInfoLabel"));
    probeLabel_->setMargin(0);
    probeLabel_->setAlignment(Qt::AlignLeft | Qt::AlignVCenter);
    probeLabel_->setAutoFillBackground(true);
    probeLabel_->setTextInteractionFlags(Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard | Qt::TextSelectableByMouse);
    probeLabel_->setFrameShape(QFrame::Box);
    probeLabel_->setFrameShape(QFrame::StyledPanel);
    // probeLabel_->setVisible(false);
    probeHb->addWidget(probeLabel_, 1);
    // probeHb->addStretch(1);

    connect(probeTb_, SIGNAL(toggled(bool)),
            this, SLOT(slotProbe(bool)));

    connect(resetProbeTb_, SIGNAL(clicked(bool)),
            this, SLOT(slotResetProbe(bool)));

    connect(filterProbeTb_, SIGNAL(toggled(bool)),
            this, SLOT(slotFilterProbe(bool)));

    updateProbeLabel();
    checkButtonStatus();
}

MvQLayerDataWidget::~MvQLayerDataWidget() = default;

void MvQLayerDataWidget::updateData()
{
    if (!layer_ || !sceneItem_) {
        panelLayout_->setCurrentIndex(0);
        return;
    }

    //---------------------------------------------------------
    // Check if the panel assigned to the current layer exists
    //---------------------------------------------------------

    QMap<MgQLayerItem*, QWidget*>::iterator itP = activePanels_.find(layer_);
    if (itP != activePanels_.end()) {
        if (itP.value() == panelLayout_->currentWidget())
            return;
        else {
            panelLayout_->setCurrentWidget(itP.value());
            return;
        }
    }

    //---------------------------------------------------------
    // Create the panel
    //---------------------------------------------------------

    // Get metadata
    MetaDataCollector md;
    md["_datatype"] = "";
    md["path"] = "";
    md["hasMatrix"] = "";
    layerMetaData(md);

    // Get datatype and path
    auto it = md.find("_datatype");
    if (it == md.end()) {
        panelLayout_->setCurrentIndex(0);
        return;
    }
    std::string dataType = it->second;

    QString dataPath;
    it = md.find("path");
    if (it != md.end())
        dataPath = QString::fromStdString(it->second);

    // Get data indexes
    DataIndexCollector info;
    sceneItem_->layerDataIndexForCurrentStep(layer_, info);
    if (info.dataIndex().size() <= 0) {
        panelLayout_->setCurrentIndex(0);
        return;
    }

    // ODB
    if (dataType == "ODB_geopoints") {
        loadOdb(dataPath, info);
        adjustDataProbe();
    }
    else if (dataType == "ODB_xy") {
        it = md.find("hasMatrix");
        if (it == md.end() || it->second != "true") {
            loadOdb(dataPath, info);
        }
        adjustDataProbe();
    }
    else {
        panelLayout_->setCurrentIndex(0);
    }
}

void MvQLayerDataWidget::slotFrameChanged()
{
    updateData();
}

void MvQLayerDataWidget::layerMetaData(MetaDataCollector& md)
{
    if (!layer_ || !sceneItem_)
        return;

    sceneItem_->layerMetaDataForCurrentStep(layer_, md);
}

void MvQLayerDataWidget::loadOdb(QString path, const DataIndexCollector& info)
{
#ifdef METVIEW_ODB_NEW

    if (!layer_)
        return;

    QWidget* panel = findIdlePanel(OdbPanel);
    MvQOdbDataWidget* w = nullptr;
    if (!panel) {
        panel = new MvQOdbDataWidget(this);
        w = dynamic_cast<MvQOdbDataWidget*>(panel);
        w->setMaxChunkSizeInMb(0);  // we do not allow data chunks

        panelLayout_->addWidget(panel);

        QPair<PanelType, QWidget*> item(OdbPanel, panel);
        panels_ << item;
        activePanels_[layer_] = panel;

        connect(w, SIGNAL(dataRowSelected(int)),
                this, SLOT(slotSetIndex(int)));
    }
    else {
        w = dynamic_cast<MvQOdbDataWidget*>(panel);
        if (activePanels_.find(layer_) == activePanels_.end())
            activePanels_[layer_] = panel;
    }

    if (w) {
        w->init(path, info.dataIndex());
        if (visibleOdbColumns_.count() > 0) {
            w->setVisibleColumns(visibleOdbColumns_);
        }
    }

    panelLayout_->setCurrentWidget(panel);
#endif
}

void MvQLayerDataWidget::checkButtonStatus()
{
    if (dataProbe_ && dataProbe_->activated() && layoutItem_) {
        filterProbeTb_->setEnabled(true);
        resetProbeTb_->setEnabled(true);
    }
    else {
        filterProbeTb_->setEnabled(false);
        resetProbeTb_->setEnabled(false);
    }
}

void MvQLayerDataWidget::slotProbe(bool b)
{
    if (b) {
        if (dataProbe_) {
            dataProbe_->setActivated(true);
            // move to right position  (using the stored coordinate)
            dataProbe_->updateScenePosition();
            // reload the selection
            slotPointSelected(dataProbe_->coordinates());
        }
        else {
            createDataProbe();
            applyFilterProbe();
        }
    }
    else {
        if (dataProbe_) {
            dataProbe_->setActivated(false);
            // move to right position  (using the stored coordiante)
            dataProbe_->updateScenePosition();
            removeTmpFilterFromPanel();
        }
    }

    updateProbeLabel();
    checkButtonStatus();
}

void MvQLayerDataWidget::slotResetProbe(bool /*b*/)
{
    if (dataProbe_ && dataProbe_->activated() && layoutItem_) {
        QRectF rect = layoutItem_->mapRectToScene(layoutItem_->boundingRect());
        QPointF coord;
        layoutItem_->mapFromSceneToGeoCoords(rect.center(), coord);

        dataProbe_->setCoordinates(coord);
        slotPointSelected(coord);
    }
}

bool MvQLayerDataWidget::isProbeFiltered() const
{
    return (dataProbe_ && dataProbe_->activated() && layoutItem_ && filterProbeTb_->isChecked());
}

void MvQLayerDataWidget::slotFilterProbe(bool)
{
    applyFilterProbe();
}


void MvQLayerDataWidget::applyFilterProbe()
{
    if (dataProbe_ && dataProbe_->activated() && layoutItem_) {
        slotPointSelected(dataProbe_->coordinates());
    }
}

void MvQLayerDataWidget::adjustDataProbe()
{
    if (dataProbe_ && dataProbe_->activated() && layoutItem_) {
        // QRectF rect=layoutItem_->mapRectToScene(layoutItem_->boundingRect());
        // QPointF coord;
        // layoutItem_->mapFromSceneToGeoCoords(rect.center(),coord);

        // dataProbe_->setCoordinates(dataProbe_->coordinates());
        // slotPointSelected(dataProbe_->coordinates());
        // slotPointSelected(dataProbe_->coordinates());
    }
}

void MvQLayerDataWidget::createDataProbe()
{
    if (dataProbe_ || !sceneItem_)
        return;

    if (layoutItem_) {
        QRectF rect = layoutItem_->mapRectToScene(layoutItem_->boundingRect());
        QPointF coord;
        layoutItem_->mapFromSceneToGeoCoords(rect.center(), coord);

        dataProbe_ = new MvQPlaceMark(plotScene_, plotView_, nullptr);
        // dataProbe_->setFlag(QGraphicsItem::ItemIgnoresTransformations);
        dataProbe_->setZValue(1.8);
        dataProbe_->setActivated(true);
        dataProbe_->setCoordinates(coord);
        dataProbe_->reset(layoutItem_);

        connect(dataProbe_, SIGNAL(pointSelected(QPointF)),
                this, SLOT(slotPointSelected(QPointF)));

        connect(dataProbe_, SIGNAL(beingMoved(QPointF)),
                this, SLOT(slotUpdateProbeLabel(QPointF)));

        plotView_->setDataProbe(dataProbe_);
        // dataProbe_->setParentItem(plotScene_->annotationRootItem());
        plotScene_->addItem(dataProbe_);
    }
}

void MvQLayerDataWidget::updateProbe()
{
    /*if(dataProbe_ && dataProbe_->activated())
    {
        dataProbe_->setCoordinates(dataProbe_->coordinates());
        updateProbeLabel();
    }*/
}

void MvQLayerDataWidget::updateProbeLabel()
{
    if (dataProbe_ && dataProbe_->activated())
        slotUpdateProbeLabel(dataProbe_->coordinates());
    else
        slotUpdateProbeLabel(QPointF());
}

void MvQLayerDataWidget::slotUpdateProbeLabel(QPointF cr)
{
    QString s;

    if (!layer_ || !sceneItem_) {
        s = tr("<i>No plot is available</i>");
    }
    else if (dataProbe_ && dataProbe_->activated()) {
        if (dataProbe_->isOutOfPlot()) {
            s = tr("<i>Probe is out of plot</i>");
        }
        else {
            s = tr("Probe position: ");
            s += "<b>" + coordXName_ + ": </b>";
            s += QString::number(cr.x());
            s += "  <b>" + coordYName_ + ": </b>";
            s += QString::number(cr.y());
        }
    }
    else {
        s = tr("<i>Probe is disabled</i>");
    }

    probeLabel_->setText(s);
}

void MvQLayerDataWidget::slotPointSelected(QPointF coord)
{
    if (!layer_ || !sceneItem_) {
        return;
    }

    updateProbeLabel();

    if (dataProbe_ &&
        (!dataProbe_->activated() || dataProbe_->isOutOfPlot())) {
        return;
    }

    QMap<MgQLayerItem*, QWidget*>::iterator it = activePanels_.find(layer_);
    if (it == activePanels_.end())
        return;

    QWidget* panel = it.value();
    PanelType type = panelType(panel);

    if (type == EmptyPanel)
        return;

    // Get the index for the position
    double searchRadiusX = 2.;
    double searchRadiusY = 2.;

    float plotScale = plotScene_->plotScale();
    double cx = layoutItem_->coordRatioX();
    double cy = layoutItem_->coordRatioY();

    if (cx != 0.) {
        searchRadiusX = 20. / fabs(cx * plotScale);
    }
    if (cy != 0.) {
        searchRadiusY = 20. / fabs(cy * plotScale);
    }


    ValuesCollector vc;

    if (isProbeFiltered()) {
        vc.setMultiData(true);
    }
    vc.setSearchRadius(searchRadiusX, searchRadiusY);
    vc.push_back(ValuesCollectorPoint(coord.x(), coord.y()));

    sceneItem_->collectLayerDataForCurrentStep(layer_, vc);

    // Pass the index to the panel widget
    std::vector<int> indexVec;
    if (vc.size() > 0 && vc[0].size() > 0) {
        for (unsigned int i = 0; i < vc[0].size(); i++) {
            indexVec.push_back(vc[0][i]->index());
        }
        QMap<MgQLayerItem*, QWidget*>::iterator it = activePanels_.find(layer_);
        if (it == activePanels_.end())
            return;

        QWidget* panel = it.value();
        PanelType type = panelType(panel);

        if (type == EmptyPanel)
            return;
    }
    setIndexForPanel(indexVec, type, panel);
}

void MvQLayerDataWidget::slotSetIndex(int index)
{
    if (!dataProbe_ || !dataProbe_->activated() || !layer_ || !sceneItem_) {
        return;
    }

    ValuesCollector vc;
    vc.push_back(ValuesCollectorPoint(index));

    sceneItem_->collectLayerDataForCurrentStep(layer_, vc);

    // Pass the index to the panel widget
    if (vc.size() > 0 && vc.at(0).size() > 0) {
        double x = vc.at(0).at(0)->x();
        double y = vc.at(0).at(0)->y();

        QPointF pos(x, y);
        dataProbe_->setCoordinates(pos);
        updateProbeLabel();
    }
}

void MvQLayerDataWidget::setIndexForPanel(const std::vector<int>& indexVec, PanelType type, QWidget* panel)
{
    // if(indexVec.empty())
    //	  return;

    if (type == OdbPanel) {
#ifdef METVIEW_ODB_NEW
        auto* data = dynamic_cast<MvQOdbDataWidget*>(panel);
        if (data) {
            if (isProbeFiltered()) {
                data->setTmpFilter(indexVec);
            }
            else {
                data->clearTmpFilter();
            }

            if (!indexVec.empty())
                data->highlightRow(indexVec[0]);

            data->setTotalNumDisplay();
        }
#endif
    }
}

void MvQLayerDataWidget::removeTmpFilterFromPanel()
{
    QMap<MgQLayerItem*, QWidget*>::iterator it = activePanels_.find(layer_);
    if (it == activePanels_.end())
        return;

    QWidget* panel = it.value();
    PanelType type = panelType(panel);

    if (type == EmptyPanel)
        return;

    std::vector<int> indexVec;
    setIndexForPanel(indexVec, type, panel);
}

//===============================================
//
// Reset
//
//===============================================

void MvQLayerDataWidget::setLayer(MgQLayerItem* layer)
{
    layer_ = layer;

    if (!layer_) {
        updateData();
        return;
    }

    updateData();
}

MvQLayerDataWidget::PanelType MvQLayerDataWidget::panelType(QWidget* w)
{
    QPair<PanelType, QWidget*> p;
    foreach (p, panels_) {
        if (p.second == w) {
            return p.first;
        }
    }
    return EmptyPanel;
}

QWidget* MvQLayerDataWidget::findIdlePanel(PanelType type)
{
    QPair<PanelType, QWidget*> p;
    foreach (p, panels_) {
        if (p.first == type) {
            QMap<MgQLayerItem*, QWidget*>::iterator it = activePanels_.find(layer_);
            if (it == activePanels_.end()) {
                return p.second;
            }
        }
    }

    return nullptr;
}

QWidget* MvQLayerDataWidget::findActivePanel(PanelType type)
{
    QPair<PanelType, QWidget*> p;
    foreach (p, panels_) {
        if (p.first == type) {
            if (activePanels_.values().indexOf(p.second) != -1) {
                return p.second;
            }
        }
    }
    return nullptr;
}

void MvQLayerDataWidget::clear()
{
    activePanels_.clear();
}

void MvQLayerDataWidget::reset(MgQSceneItem* sceneItem, MgQLayerItem* layer)
{
    sceneItem_ = sceneItem;
    layer_ = layer;

    // Save some data
    storePanelProperties();

    activePanels_.clear();
    setLayer(layer_);

    layoutItem_ = nullptr;
    if (sceneItem_) {
        layoutItem_ = sceneItem_->firstProjectorItem();

        if (layoutItem_) {
            const Transformation& transformation = layoutItem_->layout().transformation();
            switch (transformation.coordinateType()) {
                case Transformation::GeoType:
                    coordXName_ = tr("Lon");
                    coordYName_ = tr("Lat");
                    break;
                case Transformation::XyType:
                default:
                    coordXName_ = tr("X");
                    coordYName_ = tr("Y");

                    break;
            }

            if (dataProbe_) {
                dataProbe_->reset(layoutItem_);
                updateProbeLabel();
            }
        }
    }
}

void MvQLayerDataWidget::storePanelProperties()
{
    QWidget* panel = findActivePanel(OdbPanel);
    if (panel) {
#ifdef METVIEW_ODB_NEW
        auto* data = dynamic_cast<MvQOdbDataWidget*>(panel);
        if (data) {
            visibleOdbColumns_ = data->visibleColumns();
        }
#endif
    }
}

void MvQLayerDataWidget::writeSettings(QSettings& settings)
{
    settings.beginGroup("MvQLayerDataWidget");

    // Save some data
    storePanelProperties();

    if (visibleOdbColumns_.count() > 0) {
        settings.setValue("visibleOdbColumns", visibleOdbColumns_);
    }
    settings.setValue("filterProbe", filterProbeTb_->isChecked());

    settings.endGroup();
}

void MvQLayerDataWidget::readSettings(QSettings& settings)
{
    settings.beginGroup("MvQLayerDataWidget");
    visibleOdbColumns_ = settings.value("visibleOdbColumns").toStringList();

    if (settings.contains("filterProbe")) {
        filterProbeTb_->setChecked(settings.value("filterProbe").toBool());
    }
    settings.endGroup();
}
