/*
 * Copyright (C) 2005-2020 Centre National d'Etudes Spatiales (CNES)
 *
 * This file is part of Orfeo Toolbox
 *
 *     https://www.orfeo-toolbox.org/
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "mvdTreeWidget.h"


/*****************************************************************************/
/* INCLUDE SECTION                                                           */

//
// Qt includes (sorted by alphabetic order)
//// Must be included before system/custom includes.

//
// System includes (sorted by alphabetic order)

//
// ITK includes (sorted by alphabetic order)

//
// OTB includes (sorted by alphabetic order)

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdAlgorithm.h"
#include "mvdDataStream.h"
#include "mvdTreeWidgetItem.h"

namespace mvd
{

/*
  TRANSLATOR mvd::TreeWidget

  Necessary for lupdate to be aware of C++ namespaces.

  Context comment for translator.
*/

/*****************************************************************************/
/* CONSTANTS                                                                 */

const char* TreeWidget::ITEM_MIME_TYPE = "application/x-qtreewidgetitemptrlist";

/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */

#if USE_CUSTOM_MIME_DATA

class StaticInitializer
{
public:
  StaticInitializer() : m_QTreeWidgetItemPtrMetaTypeId(-1)
  {
    Initialize();
  }

  ~StaticInitializer()
  {
    Finalize();
  }

private:
  inline void Initialize()
  {
    //
    // Call qRegisterMetaType<>() to make type available in
    // non-template signatures and serialization.
    m_QTreeWidgetItemPtrMetaTypeId = qRegisterMetaType<QTreeWidgetItem*>("QTreeWidgetItem*");

//
// Register serialization operators for custom meta-types.
#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0)
    qRegisterMetaTypeStreamOperators<QTreeWidgetItem*>();
#else  // QT_VERSION >= QT_VERSION_CHECK( 4, 7, 0 )
    qRegisterMetaTypeStreamOperators<QTreeWidgetItem*>(QMetaType::typeName(m_QTreeWidgetItemPtrMetaTypeId));
#endif // QT_VERSION >= QT_VERSION_CHECK( 4, 7, 0 )
  }

  inline void Finalize()
  {
  }

  int m_QTreeWidgetItemPtrMetaTypeId;
};

namespace
{
static const StaticInitializer STATIC_INITIALIZER;
}

#endif // USE_CUSTOM_MIME_DATA

/*****************************************************************************/
QMimeData* EncodeMimeData(QMimeData* mimeData, const QList<QTreeWidgetItem*>& items)
{
#if USE_CUSTOM_MIME_DATA

  assert(mimeData != NULL);

  typedef QList<QTreeWidgetItem*> QTreeWidgetItemList;

  QByteArray  byteArray;
  QDataStream stream(&byteArray, QIODevice::WriteOnly);

  for (QTreeWidgetItemList::const_iterator it(items.begin()); it != items.end(); ++it)
  {
/*
qDebug()
  << "QTreeWidgetItem::parent()==" << ( *it )->parent();
qDebug()
  << "Pointer:" << static_cast< void* >( *it );
qDebug()
  << "Variant:" << QVariant::fromValue< QTreeWidgetItem* >( *it );
*/

#if 1
    QTreeWidgetItem* item = *it;

    qDebug() << "Item (encoded):" << item << "\n"
             << "text[ 0 ]:" << item->text(0) << "\n"
             << "text[ 1 ]:" << item->text(1) << "\n"
             << "text[ 2 ]:" << item->text(2);

    if (item->parent() != NULL)
    {
      qDebug() << "parent:" << item->parent() << "\n"
               << "text[ 0 ]:" << item->parent()->text(0) << "\n"
               << "text[ 1 ]:" << item->parent()->text(1);
    }
#endif

    // http://www.qtfr.org/viewtopic.php?id=9630
    // stream << *it;
    stream << QVariant::fromValue<QTreeWidgetItem*>(*it);
  }

  mimeData->setData(TreeWidget::ITEM_MIME_TYPE, byteArray);

/*
qDebug() << mimeData->formats();

for( QTreeWidgetItemList::const_iterator it( items.begin() );
     it!=items.end();
     ++it )
  {
  QTreeWidgetItem* item = *it;

  qDebug()
    << item->type() << item->text( 0 ) << item->text( 1 ) << item->text( 2 );
  }
*/

#endif // USE_CUSTOM_MIME_DATA

  return mimeData;
}

/*****************************************************************************/
int DecodeMimeData(QList<QTreeWidgetItem*>& items, const QMimeData* mimeData)
{
  assert(mimeData != NULL);

  int count = 0;

#if USE_CUSTOM_MIME_DATA

  if (!mimeData->hasFormat(TreeWidget::ITEM_MIME_TYPE))
    return 0;

  QByteArray byteArray(mimeData->data(TreeWidget::ITEM_MIME_TYPE));

  QDataStream stream(&byteArray, QIODevice::ReadOnly);

  //
  // http://www.qtcentre.org/threads/8756-QTreeWidgetItem-mime-type

  QTreeWidgetItem* item = NULL;

  while (!stream.atEnd())
  {
    QVariant variant;

    stream >> variant;

    // qDebug() << "Variant:" << variant;

    // http://www.qtfr.org/viewtopic.php?id=9630

    item = variant.value<QTreeWidgetItem*>();
    assert(item != NULL);

    items.push_back(item);

#if 1
    qDebug() << "Item (decoded):" << item << "\n"
             << "text[ 0 ]:" << item->text(0) << "\n"
             << "text[ 1 ]:" << item->text(1) << "\n"
             << "text[ 2 ]:" << item->text(2);

    if (item->parent() != NULL)
    {
      qDebug() << "parent:" << item->parent() << "\n"
               << "text[ 0 ]:" << item->parent()->text(0) << "\n"
               << "text[ 1 ]:" << item->parent()->text(1);
    }
#endif

    ++count;
  }

// qDebug() << count2 << "items.";

#else // USE_CUSTOM_MIME_DATA

/*
if( !mimeData->hasFormat( "application/x-qabstractitemmodeldatalist" ) )
  return 0;

QByteArray byteArray(
  mimeData->data( "application/x-qabstractitemmodeldatalist" )
);

QDataStream stream( &byteArray, QIODevice::ReadOnly );

//
// http://www.qtcentre.org/threads/8756-QTreeWidgetItem-mime-type

QTreeWidgetItem* item = NULL;

while( !stream.atEnd() )
  {
  ++ count;
  }
*/

#endif // USE_CUSTOM_MIME_DATA

  return count;
}

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */

/*******************************************************************************/
TreeWidget::TreeWidget(QWidget* p) : QTreeWidget(p)
{
  setSelectionBehavior(QAbstractItemView::SelectRows);

  // MANTIS-929: Leave default behaviour
  //
  // setDefaultDropAction( Qt::MoveAction );
  // setDefaultDropAction( Qt::CopyAction );
  // setDefaultDropAction( Qt::LinkAction );

  // MANTIS-929: Setting inspired from QGis; don't understand why
  // there is one call with InternalMode and another with DragDrop but
  // think it might change setting drag-enabled and accept-drop
  // behaviour.
  //
  setDragDropMode(QAbstractItemView::InternalMove);
  setDragEnabled(true);
  setAcceptDrops(true);
  setDragDropMode(QAbstractItemView::DragDrop);
}

/*******************************************************************************/
TreeWidget::~TreeWidget()
{
}

/*******************************************************************************/
QStringList TreeWidget::mimeTypes() const
{
  // qDebug() << this << "::mimeTypes()";

  QStringList mTypes(QTreeWidget::mimeTypes());

#if USE_CUSTOM_MIME_DATA

  mTypes << TreeWidget::ITEM_MIME_TYPE;

#endif // USE_CUSTOM_MIME_DATA

  return mTypes;
}

/*******************************************************************************/
QMimeData* TreeWidget::mimeData(const QList<QTreeWidgetItem*> itemList) const
{
  // qDebug() << this << "::mimeData(" << itemList << ")";

  return EncodeMimeData(QTreeWidget::mimeData(itemList), itemList);
}

/*******************************************************************************/
Qt::DropActions TreeWidget::supportedDropActions() const
{
  // This method is overloaded only to spy/debug Qt behavior.

  Qt::DropActions supportedActions = QTreeWidget::supportedDropActions();

  qDebug() << this << "::supportedDropActions():" << supportedActions;

  return supportedActions;
}

/*******************************************************************************/
void TreeWidget::startDrag(Qt::DropActions supportedActions)
{
  // This method is overloaded only to spy/debug Qt behavior.

  qDebug() << this << "::startDrag(" << supportedActions << ")";

  QTreeWidget::startDrag(supportedActions);
}

/*******************************************************************************/
void TreeWidget::dragEnterEvent(QDragEnterEvent* e)
{
  qDebug() << this << "::dragEnterEvent(" << e << ")";

  // e->acceptProposedAction();

  QTreeWidget::dragEnterEvent(e);
}

/*******************************************************************************/
void TreeWidget::dragMoveEvent(QDragMoveEvent* e)
{
  assert(e != NULL);

  qDebug() << this << "::dragMoveEvent(" << e << ")";
  qDebug() << this << itemAt(e->pos());

  QTreeWidget::dragMoveEvent(e);

  QTreeWidgetItem* item = itemAt(e->pos());
// const QMimeData * mimeData = e->mimeData();

#if USE_CUSTOM_MIME_DATA
  if (e->mimeData()->hasFormat(TreeWidget::ITEM_MIME_TYPE) && item != NULL)
#else  // USE_CUSTOM_MIME_DATA
  if (item != NULL)
#endif // USE_CUSTOM_MIME_DATA
  {
    qDebug() << "ACCEPT";
    e->accept();
  }
  else
  {
    qDebug() << "IGNORE";
    e->ignore();
  }

  /*
  if( e->source()==this )
    {
    e->setDropAction( Qt::MoveAction );
    e->accept();
    }
  */

  /*
  if( itemAt( e->pos() )==NULL )
    e->ignore();
  else
    e->accept();
  */
}

/*******************************************************************************/
void TreeWidget::dragLeaveEvent(QDragLeaveEvent* e)
{
  qDebug() << this << "::dragLeaveEvent(" << e << ")";

  QTreeWidget::dragLeaveEvent(e);
}

/*******************************************************************************/
void TreeWidget::dropEvent(QDropEvent* e)
{
  assert(e != NULL);

  // qDebug() << this << "::dropEvent(" << e << ")";

  QTreeWidgetItemList itemList;

  DecodeMimeData(itemList, e->mimeData());

  if (e->source() == this)
  {
    e->setDropAction(Qt::MoveAction);
    // e->accept();
  }

  qDebug() << "dropAction:" << e->dropAction();

  QTreeWidget::dropEvent(e);

  qDebug() << "dropAction:" << e->dropAction();

  /*
  QTreeWidgetItem * item = itemAt( e->pos() );

  while( !item->flags().testFlag( Qt::ItemIsDropEnabled ) )
    {
    item = item->parent();
    assert( item!=NULL );

#if 1
    qDebug()
      << "Item (target):"
      << item << "\n"
      << "text[ 0 ]:" << item->text( 0 ) << "\n"
      << "text[ 1 ]:" << item->text( 1 ) << "\n"
      << "text[ 2 ]:" << item->text( 2 ) << "\n"
      << "parent:" << item->parent() << "\n"
      << "text[ 0 ]:" << item->parent()->text( 0 ) << "\n"
      << "text[ 1 ]:" << item->parent()->text( 1 );
#endif
    }
  */

  /*
  for( QTreeWidgetItemList::const_iterator it = itemList.begin();
       it!=itemList.end();
       ++it )
    {
    switch( e->dropAction() )
      {
      case Qt::MoveAction:
        emit ItemMoved( *it, itemAt( e->pos() ) );
        break;

      default:
        break;
      }
    }
  */

  QTreeWidgetItem* target = itemAt(e->pos());

  qDebug() << "itemAt(" << e->pos() << "):" << target;

  if (e->source() == this)
    for (QTreeWidgetItemList::const_iterator it = itemList.begin(); it != itemList.end(); ++it)
      emit ItemMoved(*it, target);
}

/*******************************************************************************/
/* SLOTS                                                                       */
/*******************************************************************************/

} // end namespace 'mvd'

/*****************************************************************************/
/* GLOBAL FUNCTIONS IMPLEMENTATION SECTION                                   */

#if USE_CUSTOM_MIME_DATA

#if TREE_WIDGET_ITEM_USE_STREAM_OPERATORS

/*****************************************************************************/
QDataStream& operator<<(QDataStream& out, QTreeWidgetItem const* item)
{
/*
qDebug() <<
  "QDataStream& operator << ( QDataStream&, QTreeWidgetItem const * & );";
*/

#if 0 // operator >> is used in QT5 this lead to wrong call, fix: comment or
// put operator >> def and decl in a specific namespace.
  return operator << < QTreeWidgetItem >( out, item );

#else // DATA_STREAM_USE_TEMPLATE_OPERATORS
  DATA_STREAM_OUT(out, QTreeWidgetItem, item);

  return out;

#endif // DATA_STREAM_USE_TEMPLATE_OPERATORS
}

/*****************************************************************************/
QDataStream& operator>>(QDataStream& in, QTreeWidgetItem*& item)
{
/*
qDebug() <<
  "QDataStream& operator >> ( QDataStream&, QTreeWidgetItem * & );";
*/

#if 0
  return operator >> < QTreeWidgetItem >( in, item );

#else // DATA_STREAM_USE_TEMPLATE_OPERATORS
  DATA_STREAM_IN(in, QTreeWidgetItem, item);

  return in;

#endif // DATA_STREAM_USE_TEMPLATE_OPERATORS
}

#endif // TREE_WIDGET_ITEM_USE_STREAM_OPERATORS

#endif // USE_CUSTOM_MIME_DATA
