/***************************** 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 <QApplication>
#include <QBrush>
#include <QColor>
#include <QMimeData>
#include <QMouseEvent>
#include <QPainter>
#include <QStringList>
#include <QStyleOptionViewItem>

#include "MgQLayerItem.h"
#include "MgQPlotScene.h"
#include "MgQSceneItem.h"

#include "MvQDragDrop.h"
#include "MvQDropTarget.h"
#include "MvQLayerContentsIcon.h"
#include "MvQLayerModel.h"
#include "ObjectList.h"
#include "PlotMod.h"
#include "Root.h"
#include "MvLog.h"

//======================================
// Item delegate
//======================================

MvQLayerDelegate::MvQLayerDelegate(QWidget* parent) :
    QStyledItemDelegate(parent)
{
    itemHeight_ = 50 + 8;
}

void MvQLayerDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    int id = index.data(MvQLayerModel::LevelRole).toInt();
    if (id == 0 && index.column() == 0) {
        QStyleOptionViewItem vopt(option);
        initStyleOption(&vopt, index);

        const QStyle* style = vopt.widget ? vopt.widget->style() : QApplication::style();
        const QWidget* widget = vopt.widget;

        // int height=option.rect.height();

        // vopt.text=QString();
        // vopt.icon=QIcon();

        // We render everything with the default method
        style->drawControl(QStyle::CE_ItemViewItem, &vopt, painter, widget);

        // Get current values from model
        // QString txt=index.data(Qt::DisplayRole).toString();
        // QPixmap pixmap=index.data(Qt::DecorationRole).value<QPixmap>();
        // QBrush bg=index.data(Qt::BackgroundRole).value<QBrush>();

        // Save painter state
        painter->save();

        // Highlight
        // if (option.state & QStyle::State_HasFocus)
        //      painter->fillRect(option.rect, option.palette.highlight());

        // QRect checkRect = style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator,&vopt, widget);
        QRect iconRect = style->subElementRect(QStyle::SE_ItemViewItemDecoration, &vopt, widget);
        // QRect textRect = style-> subElementRect(QStyle::SE_ItemViewItemText, &vopt, widget);

        // Draw icon border
        painter->setPen(QColor(230, 230, 230));
        painter->drawRect(iconRect);

        // Text
        /*QString text=index.data(Qt::DisplayRole).toString();
        QRect textRect(iconRect.x()+iconRect.width()+10,option.rect.y()+5,
                   option.rect.width()-(iconRect.x()+iconRect.width()+5)-10, height/2-5);

        painter->setPen(Qt::black);
        painter->drawText(textRect,Qt::AlignLeft,text);*/

        // Draw separator line
        painter->setPen(QColor(230, 230, 230));
        painter->drawLine(option.rect.bottomLeft(), option.rect.bottomRight());

        // Restore painter state
        painter->restore();
    }
    else {
        QStyledItemDelegate::paint(painter, option, index);
    }
}

QSize MvQLayerDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const
{
    QSize size = QStyledItemDelegate::sizeHint(option, index);

    int id = index.internalId();
    if (id < 100 && index.column() == 0) {
        size.setHeight(itemHeight_);
    }
    else {
        size += QSize(0, 6);
    }

    return size;
}

//===========================================================
//
// MvQLayerModel model
//
//===========================================================

MvQLayerModel::MvQLayerModel(MgQPlotScene* scene) :
    scene_(scene),
    sceneItem_(nullptr),
    higlightedId_(-1),
    presentableId_(1)
{
}

MvQLayerModel::~MvQLayerModel()
{
    clearIcons();
}

void MvQLayerModel::clearIcons()
{
    QHashIterator<MgQLayerItem*, QList<MvQLayerContentsIcon*> > it(icons_);
    while (it.hasNext()) {
        it.next();
        foreach (MvQLayerContentsIcon* icon, it.value()) {
            if (icon)
                delete icon;
        }
    }

    icons_.clear();
}

void MvQLayerModel::layersAreAboutToChange()
{
    beginResetModel();
}

void MvQLayerModel::resetLayers(MgQSceneItem* scenItem)
{
    sceneItem_ = scenItem;

    if (sceneItem_ != nullptr) {
        presentableId_ = atoi(sceneItem_->layout().id().c_str());
        clearIcons();
        if (sceneItem_) {
            layers_ = sceneItem_->layerItems();
        }
        else {
            layers_.clear();
        }
    }
    else {
        clearIcons();
        layers_.clear();
    }
    endResetModel();
}

int MvQLayerModel::layerToRow(MgQLayerItem* layer) const
{
    if (!layer)
        return -1;

    int stackLevel = layer->stackLevel();

    return layers_.count() - 1 - stackLevel;
}

MgQLayerItem* MvQLayerModel::rowToLayer(int row) const
{
    //
    int stackLevel = layers_.count() - 1 - row;

    foreach (MgQLayerItem* item, layers_) {
        if (item->stackLevel() == stackLevel) {
            return item;
        }
    }
    return nullptr;
}

int MvQLayerModel::rowToStackLevel(int row)
{
    //
    return layers_.count() - 1 - row;
}

int MvQLayerModel::columnCount(const QModelIndex& /* parent */) const
{
    return 1;
}

int MvQLayerModel::rowCount(const QModelIndex& parent) const
{
    if (!sceneItem_)
        return 0;

    // Non-root
    if (parent.isValid()) {
        int id = parent.internalId();
        if (idToLevel(id) == 0) {
#if 1
            // Get number of icons related to this layer
            MgQLayerItem* layer = rowToLayer(id);
            if (!layer)
                return 0;

            int layerId = atoi(layer->layer().id().c_str());
            Presentable* pres = Root::Instance().FindSuperPage();
            return pres->RetrieveNumberIconsFromLayer(layerId);
#else
            MgQIconList icons;
            MgQLayerItem* item = rowToLayer(id);
            if (item) {
                sceneItem_->layerIconsForCurrentStep(item, icons);
            }
            return icons.size();
#endif
        }
        else {
            return 0;
        }
    }
    // Root
    else {
        return layers_.count();
    }
}

Qt::ItemFlags MvQLayerModel::flags(const QModelIndex& index) const
{
    Qt::ItemFlags defaultFlags;

    defaultFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
    //      | Qt::ItemIsEditable;

    if (index.isValid())
        return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
    else
        return Qt::ItemIsDropEnabled | defaultFlags;

    //   return defaultFlags;
}

QVariant MvQLayerModel::data(const QModelIndex& index, int role) const
{
    if (!index.isValid() || index.column() != 0)
        return {};

    int id = index.internalId();
    if (idToLevel(id) == 0) {
        MgQLayerItem* layer = rowToLayer(id);
        if (!layer)
            return {};

        if (role == Qt::CheckStateRole) {
            if (layer->layerVisibility() == true)
                return {Qt::Checked};
            else
                return {Qt::Unchecked};
        }
        else if (role == Qt::DisplayRole) {
            return label(layer, index.row(), index.column());
        }
        else if (role == Qt::DecorationRole) {
            return QPixmap::fromImage(layer->preview());
        }
        else if (role == Qt::BackgroundRole) {
            if (higlightedId_ == id)
                return QColor(230, 230, 230);
            else
                return {};
        }
        else if (role == VisibleRole) {
            return checkLayerVisibility(layer);
            //         return true;   //always show icons level 0
        }
        else if (role == LevelRole)
            return 0;
    }
    else if (idToLevel(id) == 1) {
        int parentRow = idToParentRow(id);
        MgQLayerItem* layer = rowToLayer(parentRow);
        if (!layer)
            return {};

        MvQLayerContentsIcon* icon = layerIcon(layer, index.row());
        if (!icon)
            return {};

        if (role == Qt::DisplayRole) {
            return icon->name();
        }
        else if (role == Qt::DecorationRole) {
            return icon->pixmap();
        }
        else if (role == Qt::UserRole) {
            return icon->path();
        }
        else if (role == VisibleRole)
            return showIcon(icon);

        else if (role == LevelRole)
            return 1;
    }

    /*else if(role == Qt::ToolTipRole)
    {
        MgQLayerItem* layer=rowToLayer(index.row());

        map<std::string,string>::const_iterator it=layer->layer().getInfos().find("Desc");

        if( it!= layer->layer().getInfos().end())
        {
            return 	QString::fromStdString(it->second);
        }

        return QVariant();
    }*/

    return {};
}

bool MvQLayerModel::setData(const QModelIndex& index, const QVariant& value, int role)
{
    if (!index.isValid()) {
        return false;
    }

    int id = index.internalId();
    if (idToLevel(id) != 0)
        return false;

    if (index.column() == 0) {
        if (role == Qt::CheckStateRole) {
            MgQLayerItem* layer = rowToLayer(index.row());
            bool checked = (value.toInt() == Qt::Checked) ? true : false;
            if (checked != layer->layerVisibility()) {
                layer->setLayerVisibility(checked);
                emit dataChanged(index, index);

                // Notify the scene about the change in the layer status

                if (sceneItem_)
                    sceneItem_->updateLayers();

                // emit  layerUpdate();
                emit layerVisibilityChanged(QString(layer->layer().id().c_str()), checked);

                return true;
            }
        }
    }

    return false;
}

#if 0
QVariant MvQLayerModel::headerData( const int section, const Qt::Orientation orient , const int role ) const
{
   if ( orient != Qt::Horizontal || role != Qt::DisplayRole )
      return QAbstractItemModel::headerData( section, orient, role );

   switch (section)
   {
      case 0:
         return QString("Data Level");

      default:
         return QString();
   }

   return QVariant();
}
#endif

QString MvQLayerModel::label(MgQLayerItem* layer, const int /*row*/, const int column) const
{
    if (column == 0) {
        QString str(layer->layer().name().c_str());
        QStringList lst = str.split("/");
        if (!lst.isEmpty()) {
            return lst.last();
        }
    }

    return {};
}

MvQLayerContentsIcon* MvQLayerModel::layerIcon(MgQLayerItem* layer, int index) const
{
    if (index < 0 || !layer)
        return nullptr;

    // Retrieve icon if it is already cashed
    QHash<MgQLayerItem*, QList<MvQLayerContentsIcon*> >::const_iterator it = icons_.find(layer);
    if (it != icons_.end()) {
        if (index < it.value().count()) {
            // qDebug() << "XXXXXXXXXXXX" << it.value().at(index->name() << it.value().at(index->type() << it.value().at(index->id();

            return it.value().at(index);
        }
    }

    // Icons have not been cashed yet
    // Get list of icons related to this layer
    MvIconList iconList;
    int layerId = atoi(layer->layer().id().c_str());
    Presentable* pres = Root::Instance().FindSuperPage();
    pres->RetrieveIconsFromLayer(layerId, iconList);

    // Copy icons to the class structure (i.e. cash them).
    // For each icon add a tag indicating if it is to be shown.
    // For example, if there is a data followed by a visdef then
    // a) data will be shown
    // b) visdef will be shown if it is connected to the data,
    //    not to the presentable
    int dataId = -1;
    auto lCursor = iconList.begin();
    while (lCursor != iconList.end()) {
        // qDebug() << "YYYYYYYYY" << iconList.size() << (*lCursor).IconName() << (*lCursor).IconClass() << (*lCursor).Id();
        //  Create icon structure
        char sId[24];
        sprintf(sId, "%d", (*lCursor).Id());

        // Check if this icon came from a Macro
        MvRequest req = (*lCursor).Request();
        bool fromMacro = false;
        if (((const char*)req("_APPL") && strcmp((const char*)req("_APPL"), "macro") == 0) ||
            ((const char*)req("_CALLED_FROM_MACRO") && (int)req("_CALLED_FROM_MACRO") == 1))
            fromMacro = true;

        auto* icon = new MvQLayerContentsIcon((*lCursor).IconName(), (*lCursor).IconClass(), sId, fromMacro);

        // Check if icon is to be shown
        bool bvis = this->checkIconVisibility(icon, dataId);
        icon->visibility(bvis);

        // Add icon to the list
        icons_[layer] << icon;
        ++lCursor;
    }

    it = icons_.find(layer);
    if (it != icons_.end()) {
        if (index < it.value().count())
            return it.value().at(index);
    }

    return nullptr;
}

bool MvQLayerModel::showIcon(MvQLayerContentsIcon* icon) const
{
    return icon->visibility();
}


bool MvQLayerModel::checkIconVisibility(MvQLayerContentsIcon* icon, int& dataId) const
{
    // Get presentable Id related to this icon
    int newDataId = -1;
    Presentable* pres = Root::Instance().FindSuperPage();
    int branchId = pres->FindBranchIdByIconId(icon->id().toInt(), newDataId);
    if (branchId != presentableId_)
        return false;  // Icon does not belong to current presentable

    // Icon belongs to the current presentable.
    // Check if icon is to be shown according to its CLASS
    Cached cclass = icon->type().toStdString().c_str();

    if (ObjectList::IsDataUnit(cclass)) {
        dataId = icon->id().toInt();
        return true;
    }
    else if (ObjectList::IsVisDefAxis(cclass))
        return false;  // FAMI20161021 temporary while axis handling is not implemented
    else if (ObjectList::IsVisDef(cclass))
        return (dataId == newDataId ? true : false);  // is visdef connected to the current data?
    else
        return false;
}

// Temporary function
// All the layers should be shown by default.
// The problem here is that we can not handle the AXES at the moment.
// Remove this function when we add code to handle the AXES.
bool MvQLayerModel::checkLayerVisibility(MgQLayerItem* layer) const
{
    if (!layer)
        return false;

    // qDebug() << layer->name();

    // Do not show AXES layers
    std::string name = layer->name().toStdString();
    if (name.empty() || name == "VERTICAL_AXIS" || name == "HORIZONTAL_AXIS" || name == "UNKNOW")
        return false;

    return true;
}

MvQLayerContentsIcon* MvQLayerModel::indexToIcon(const QModelIndex& index) const
{
    if (!index.isValid() || index.column() != 0) {
        return nullptr;
    }

    int id = index.internalId();
    if (idToLevel(id) == 1) {
        int parentRow = idToParentRow(id);
        MgQLayerItem* layer = rowToLayer(parentRow);
        if (!layer)
            return nullptr;

        return layerIcon(layer, index.row());
    }

    return nullptr;
}

QModelIndex MvQLayerModel::index(int row, int column, const QModelIndex& parent) const
{
    if (!sceneItem_ || row < 0 || column < 0 || parent.column() > 3) {
        return {};
    }

    // Parent is non-root -> level-1 items: id is the parent row (number+1)*1000
    if (parent.isValid()) {
        int id = (parent.row() + 1) * 1000;
        return createIndex(row, column, id);
    }
    // Parent is root -> level-0 items: id is the row number
    else {
        return createIndex(row, column, row);
    }
}


QModelIndex MvQLayerModel::parent(const QModelIndex& index) const
{
    if (!index.isValid()) {
        return {};
    }

    int id = index.internalId();
    if (idToLevel(id) == 0) {
        return {};
    }
    else {
        int parentRow = idToParentRow(id);
        return createIndex(parentRow, 0, parentRow);
    }

    return {};
}

int MvQLayerModel::idToLevel(int id) const
{
    if (id >= 0 && id < 1000)
        return 0;
    else
        return 1;
}

int MvQLayerModel::idToParentRow(int id) const
{
    if (idToLevel(id) == 0)
        return -1;
    else
        return id / 1000 - 1;
}

int MvQLayerModel::indexToLevel(const QModelIndex& index) const
{
    return idToLevel(index.internalId());
}

MgQLayerItem* MvQLayerModel::layer(const QModelIndex& index) const
{
    if (!index.isValid())
        return nullptr;

    return rowToLayer(index.row());
}

MgQLayerItem* MvQLayerModel::layerFromAnyLevel(const QModelIndex& index) const
{
    if (!index.isValid())
        return nullptr;

    int id = index.internalId();
    if (idToLevel(id) == 1)  // Icon level, get its parent (the layer level)
        id = idToParentRow(id);

    return rowToLayer(id);
}

int MvQLayerModel::transparency(const QModelIndex& index)
{
    if (!index.isValid())
        return 0;

    MgQLayerItem* layer = rowToLayer(index.row());

    if (!layer)
        return 0;

    int value = static_cast<int>((1. - layer->layerAlpha()) * 100.);
    if (value < 0)
        value = 0;
    else if (value > 100)
        value = 100;

    return value;
}


void MvQLayerModel::setTransparency(const QModelIndex& index, int value)
{
    if (!index.isValid() || value < 0 || value > 100)
        return;

    MgQLayerItem* layer = rowToLayer(index.row());
    float alpha = 1. - static_cast<float>(value) / 100.;
    layer->setLayerAlpha(alpha);

    // Notify the scene about the change in the layer status

    if (sceneItem_)
        sceneItem_->updateLayers();

    emit layerTransparencyChanged(QString(layer->layer().id().c_str()), value);
}


void MvQLayerModel::moveUp(const QModelIndex& index)
{
    if (!index.isValid())
        return;

    moveLayer(index.row(), index.row() - 1);
}

void MvQLayerModel::moveDown(const QModelIndex& index)
{
    if (!index.isValid())
        return;

    moveLayer(index.row(), index.row() + 1);
}

void MvQLayerModel::moveTop(const QModelIndex& index)
{
    if (!index.isValid())
        return;

    moveLayer(index.row(), 0);
}

void MvQLayerModel::moveBottom(const QModelIndex& index)
{
    if (!index.isValid())
        return;

    moveLayer(index.row(), layers_.count() - 1);
}


void MvQLayerModel::moveLayer(int sourceRow, int targetRow)
{
    if (sourceRow < 0 || sourceRow >= layers_.count() ||
        targetRow < 0 || targetRow >= layers_.count() ||
        sourceRow == targetRow) {
        return;
    }

    // This list for each stackLevel assigns the corresponding Layer item
    QList<MgQLayerItem*> stackLevel;
    for (int i = 0; i < layers_.count(); i++) {
        bool found = false;
        foreach (MgQLayerItem* item, layers_) {
            if (item->stackLevel() == i) {
                stackLevel << item;
                found = true;
            }
        }
        if (found == false) {
            MvLog().popup().warn() << "uPlot MvQLayerModel::moveLayer-> Inconsistency in stackLevels";
            return;
        }
    }

    // No simpy move the sorce index to the target!!
    stackLevel.move(rowToStackLevel(sourceRow), rowToStackLevel(targetRow));

    QList<QPair<QString, int> > stackingOrder;


    // Reset the stacklevels for all the layer items!!!
    for (int i = 0; i < stackLevel.count(); i++) {
        stackLevel[i]->setStackLevel(i);
        stackingOrder << qMakePair(QString(stackLevel[i]->layer().id().c_str()), i);
    }

    emit layerStackingOrderChanged(stackingOrder);

    /*

    layers_[i]->setStackLevel(stackLevel[i]);
    }


    QList<int> stackLevel;
    foreach(MgQLayerItem *item, layers_)
    {
        stackLevel << item->stackLevel();
    }

    stackLevel.move(rowToStackLevel(sourceRow),rowToStackLevel(targetRow));

    for(int i=0; i < stackLevel.count(); i++)
    {
        layers_[i]->setStackLevel(stackLevel[i]);
    }

    */

    // Notify the scene about the change in the layer status

    if (sceneItem_)
        sceneItem_->updateLayers();

    // layers_[0]->scene()->update();

    // reset();  NOT in Qt 5
    beginResetModel();
    endResetModel();
}

std::string MvQLayerModel::layerId(const QModelIndex& index)
{
    MgQLayerItem* item = layer(index);

    if (!item)
        return {};

    return item->layer().id();
}


QString MvQLayerModel::layerName(const QModelIndex& index)
{
#if 1
    MgQLayerItem* layerItem = layerFromAnyLevel(index);
    if (layerItem)
        return layerItem->name();

#else

    MgQLayerItem* item = layer(index);
    if (!item)
        return QString();

    QString str(item->layer().name().c_str());
    QStringList lst = str.split("/");
    if (!lst.isEmpty())
        return lst.last();
#endif

    return {};
}


void MvQLayerModel::layerMetaData(const QModelIndex& index, MetaDataCollector& md)
{
    MgQLayerItem* item = layer(index);
    if (!item)
        return;

    // MetaDataCollector mdc=metaData;

    if (sceneItem_)
        sceneItem_->layerMetaDataForCurrentStep(item, md);

    // metaData=mdc;
}

QString MvQLayerModel::layerMetaData(const QModelIndex& index, QString key)
{
    MgQLayerItem* item = layer(index);

    if (item == nullptr || sceneItem_ == nullptr) {
        return {};
    }

    MetaDataCollector md;
    std::string keyStr = key.toStdString();
    md[keyStr] = "";
    sceneItem_->layerMetaDataForCurrentStep(item, md);
    return QString::fromStdString(md[keyStr]);
}


/*
const std::map<std::string,string>& MvQLayerModel::layerInfo(const QModelIndex& index)
{
    static const std::map<std::string,string> emptyMap;

    MgQLayerItem* item=layer(index);
    if(!item)
        return emptyMap;

    return scene_->getLayerInfoForCurrentStep(item);
}

QString MvQLayerModel::layerInfo(const QModelIndex& index,QString key)
{
    MgQLayerItem* item=layer(index);

    if(!item)
        return QString();

    const std::map<std::string, std::string>& itemInfo=scene_->getLayerInfoForCurrentStep(item);
    //const std::map<std::string, std::string>& itemInfo=item->layer().getInfos();
    if(!itemInfo.empty())
    {
        map<std::string,string>::const_iterator it=item->layer().getInfos().find(key.toStdString());
        if(it !=  itemInfo.end())
        {
            return QString::fromStdString(it->second);
        }
    }

    return QString();
}
*/

Qt::DropActions MvQLayerModel::supportedDropActions() const
{
    return Qt::CopyAction;
}

QStringList MvQLayerModel::mimeTypes() const
{
    QStringList types;
    types << "application/vnd.text.list";
    return types;
}

QMimeData* MvQLayerModel::mimeData(const QModelIndexList& indexes) const
{
    auto* mimeData = new QMimeData();
    QByteArray encodedData;

    QDataStream stream(&encodedData, QIODevice::WriteOnly);

    foreach (QModelIndex index, indexes) {
        if (index.isValid()) {
            QString text = QString::number(index.row());
            stream << text;
        }
    }


    mimeData->setData("application/vnd.text.list", encodedData);
    return mimeData;
}

bool MvQLayerModel::dropMimeData(const QMimeData* data,
                                 Qt::DropAction action, int row, int /*column*/, const QModelIndex& parent)
{
    if (action == Qt::IgnoreAction)
        return true;

    if (!data->hasFormat("application/vnd.text.list"))
        return false;

    int beginRow = 0;
    if (row != -1)
        beginRow = row;
    else if (parent.isValid())
        beginRow = parent.row();
    else
        beginRow = rowCount(QModelIndex());

    QByteArray encodedData = data->data("application/vnd.text.list");
    QDataStream stream(&encodedData, QIODevice::ReadOnly);
    QStringList newItems;

    int rows = 0;
    while (!stream.atEnd()) {
        QString text;
        stream >> text;
        newItems << text;
        rows++;
    }

    if (rows == 0)
        return false;

    int sourceRow = newItems[0].toInt();
    moveLayer(sourceRow, beginRow);

    emit layerDragFinished();

    return true;
}

// If id is a negative value then there will be no highlighted layers
void MvQLayerModel::setHighlightedLayer(const QModelIndex& index, int id)
{
    higlightedId_ = id;

    // Re-render
    emit dataChanged(index, index);
}


//==============================================================
//
// MvQLayerFilterModel model
//
//==============================================================

MvQLayerFilterModel::MvQLayerFilterModel(QObject* parent) :
    QSortFilterProxyModel(parent)
{
}

bool MvQLayerFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
    QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
    bool ret = sourceModel()->data(index, MvQLayerModel::VisibleRole).toBool();
    return ret;
}


//==============================================================
//
// MvQLayerView view
//
//==============================================================

MvQLayerView::MvQLayerView(QWidget* parent) :
    QTreeView(parent),
    model_(nullptr),
    filterModel_(nullptr),
    layerId_(-1)
{
    setMouseTracking(true);  // receives mouse move events even if no buttons are pressed
    setHeaderHidden(true);   // do not show/call headerData
}

void MvQLayerView::setLayerModel(MvQLayerModel* m, MvQLayerFilterModel* fm)
{
    model_ = m;
    filterModel_ = fm;
    QTreeView::setModel(fm);
}

void MvQLayerView::dragEnterEvent(QDragEnterEvent* event)
{
    if ((event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
        layerId_ = -1;  // initialise with a non-valid value
        event->accept();
    }
}

void MvQLayerView::dragLeaveEvent(QDragLeaveEvent* event)
{
    //   layerId_ = -1;   // initialise with a non-valid value
    //   model_->setHighlightedLayer(QModelIndex(),layerId_);

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

void MvQLayerView::dragMoveEvent(QDragMoveEvent* event)
{
    if ((event->proposedAction() == Qt::CopyAction ||
         event->proposedAction() == Qt::MoveAction)) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QModelIndex idx = this->indexAt(event->position().toPoint());
#else
        QModelIndex idx = this->indexAt(event->pos());
#endif
        if (!idx.isValid())  // mouse is not under the layers
        {
            removeDropTarget();
            return;
        }

        QModelIndex index = filterModel_->mapToSource(idx);
        if (layerId_ == (signed)index.internalId())  // mouse is over the same layer
            return;

        // Call the model to handle the colour highlighting.
        // The first parameter must be the QMOdelIndex parent, not a specific index.
        // This will allow the painting procedure to go through all the indexes and
        // unset the previous highlighted index and highlight only the one indicated
        // by variable layerId_.
        //      layerId_ = index.internalId();
        //      model_->setHighlightedLayer(QModelIndex(),layerId_);

        checkDropTarget(event);
        event->accept();
    }
    else {
        removeDropTarget();
        event->ignore();
    }
}

void MvQLayerView::dropEvent(QDropEvent* event)
{
    removeDropTarget();

    // First remove the layer highlighted colour
    //   layerId_ = -1;
    //   model_->setHighlightedLayer(QModelIndex(),layerId_);

    // Get dropped icon
    MvQDrop drop(event);
    if (drop.hasData()) {
        // Get index from the dropped position (the target icon)
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        QModelIndex index = filterModel_->mapToSource(this->indexAt(event->position().toPoint()));
#else
        QModelIndex index = filterModel_->mapToSource(this->indexAt(event->pos()));
#endif
        if (!index.isValid()) {
            MvLog().popup().warn() << "uPlot MvQLayerView::dropEvent-> Drop not accepted";
            return;
        }

        // To handle the dropped icon
        emit iconDropped(drop, index);

        event->accept();
        return;
    }
    else {
        // DropEvent relayed base class
        QTreeView::dropEvent(event);
    }
}

void MvQLayerView::checkDropTarget(QDropEvent* event)
{
    // Get the layer name
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
    QModelIndex idx = this->indexAt(event->position().toPoint());
#else
    QModelIndex idx = this->indexAt(event->pos());
#endif
    QModelIndex index = filterModel_->mapToSource(idx);
    QString lname = model_->layerName(index);
    if (lname.isEmpty()) {
        removeDropTarget();
        return;
    }

    MvQDropTarget::Instance()->reset(lname, QString("Apply to layer "));
    if (window()) {
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
        MvQDropTarget::Instance()->move(mapToGlobal(event->position().toPoint()) + QPoint(20, 20));
#else
        MvQDropTarget::Instance()->move(mapToGlobal(event->pos()) + QPoint(20, 20));
#endif
    }
}

void MvQLayerView::removeDropTarget()
{
    MvQDropTarget::Instance()->hide();
}

#if 0
void MvQLayerView::mouseMoveEvent(QMouseEvent* event)
{
   QModelIndex index = this->indexAt(event->pos());
   if( !index.isValid() )   // mouse is not under the layers
      return;

   if ( layerId_ == (signed)index.internalId() ) // mouse is over the same layer
      return;

   layerId_ = index.internalId();
//   model_->setHighlightedLayer(index);
}
#endif
