/*
 * Copyright (C) 2005-2017 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 "mvdLayerStackItemModel.h"


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

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

//
// System includes (sorted by alphabetic order)
#include <cassert>

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

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

//
// Monteverdi includes (sorted by alphabetic order)
#include "mvdAbstractLayerModel.h"
#include "mvdStackedLayerModel.h"
#include "mvdVectorImageModel.h"
#include "mvdVisibleInterface.h"

namespace mvd
{
/*
  TRANSLATOR mvd::LayerStackItemModel

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

  Context comment for translator.
*/


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

namespace
{

const char * const
HEADERS[ LayerStackItemModel::COLUMN_COUNT ] =
{
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Proj" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Res" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Name" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Effect" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Column" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Line" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Red" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Green" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Blue" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "X" ),
  QT_TRANSLATE_NOOP( "mvd::LayerStackItemModel", "Y" ),
};

} // end of anonymous namespace.


/*****************************************************************************/
/* STATIC IMPLEMENTATION SECTION                                             */
/*****************************************************************************/
const AbstractLayerModel *
LayerStackItemModel
::GetLayer( const QModelIndex & index )
{
  assert( index.isValid() );
  assert( !index.parent().isValid() );

  assert(
    index.internalPointer()==
    static_cast< const AbstractLayerModel * >( index.internalPointer() )
  );

  return
    static_cast< const AbstractLayerModel * >( index.internalPointer() );
}

/*****************************************************************************/
/* CLASS IMPLEMENTATION SECTION                                              */
/*****************************************************************************/
LayerStackItemModel
::LayerStackItemModel( QObject* p ) :
  QAbstractItemModel( p ),
  m_StackedLayerModel( NULL )
{
  // QObject::connect(
  //   this,
  //   SIGNAL( modelAboutToBeReset() ),
  //   // to:
  //   this,
  //   SLOT( OnModelAboutToBeReset() )
  // );

  // QObject::connect(
  //   this,
  //   SIGNAL( modelReset() ),
  //   // to:
  //   this,
  //   SLOT( OnModelReset() )
  // );
}

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

/*****************************************************************************/
void
LayerStackItemModel
::Connect( AbstractLayerModel * layer )
{
  assert( layer!=NULL );

  QObject::connect(
    layer,
    SIGNAL( VisibilityChanged( AbstractLayerModel *, bool ) ),
    // to:*
    this,
    SLOT( OnLayerVisibilityChanged( AbstractLayerModel *, bool ) )
  );

  if( layer->inherits( VectorImageModel::staticMetaObject.className() ) )
    {
    QObject::connect(
      layer,
      SIGNAL( SettingsUpdated( AbstractImageModel * ) ),
      // to:
      this,
      SLOT( OnImageSettingsUpdated( AbstractImageModel * ) )
    );
    }
}

/*****************************************************************************/
void
LayerStackItemModel
::Disconnect( AbstractLayerModel * layer )
{
  assert( layer!=NULL );

  QObject::disconnect(
    layer,
    SIGNAL( VisibilityChanged( AbstractLayerModel *,  bool ) ),
    // from:
    this,
    SLOT( OnLayerVisibilityChanged( AbstractLayerModel *, bool ) )
  );

  if( layer->inherits( VectorImageModel::staticMetaObject.className() ) )
    {
    QObject::disconnect(
      layer,
      SIGNAL( SettingsUpdated( AbstractImageModel * ) ),
      // from:
      this,
      SLOT( OnImageSettingsUpdated( AbstractImageModel * ) )
    );
    }
}

/*****************************************************************************/
void
LayerStackItemModel
::SetStack( StackedLayerModel * model )
{
  // emit layoutAboutToBeChanged();

  if( m_StackedLayerModel!=NULL )
    {
    for( StackedLayerModel::ConstIterator it( m_StackedLayerModel->Begin() );
         it!=m_StackedLayerModel->End();
         ++it )
    Disconnect( it->second );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( ContentAboutToBeReset() ),
      // from:
      this,
      SIGNAL( modelAboutToBeReset() )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( ContentReset() ),
      // from:
      this,
      SIGNAL( modelReset() )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( LayerAdded( size_t ) ),
      // from: 
      this,
      SLOT( OnLayerAdded( size_t ) )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( LayerAboutToBeDeleted( size_t ) ),
      // from: 
      this,
      SLOT( OnLayerAboutToBeDeleted( size_t ) )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( LayerDeleted( size_t ) ),
      // from: 
      this,
      SLOT( OnLayerDeleted( size_t ) )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( OrderAboutToBeChanged() ),
      // from:
      this,
      SIGNAL( layoutAboutToBeChanged() )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( OrderChanged() ),
      // from:
      this,
      SIGNAL( layoutChanged() )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( ReferenceChanged( size_t ) ),
      // from:
      this,
      SLOT( OnReferenceChanged( size_t ) )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( PixelInfoChanged( const QPoint &, const PointType &, const PixelInfo::Vector & ) ),
      // from:
      this,
      SLOT( OnPixelInfoChanged( const QPoint &, const PointType &, const PixelInfo::Vector & ) )
    );

    QObject::disconnect(
      m_StackedLayerModel,
      SIGNAL( ResolutionsChanged( const PixelInfo::Vector & ) ),
      // from:
      this,
      SLOT( ResolutionsChanged( const PixelInfo::Vector & ) )
    );
    }

  m_StackedLayerModel = model;

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( ContentAboutToBeReset() ),
    // to:
    this,
    SIGNAL( modelAboutToBeReset() )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( ContentReset() ),
    // to:
    this,
    SIGNAL( modelReset() )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( LayerAdded( size_t ) ),
    // to: 
    this,
    SLOT( OnLayerAdded( size_t ) )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( LayerAboutToBeDeleted( size_t ) ),
    // to: 
    this,
    SLOT( OnLayerAboutToBeDeleted( size_t ) )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( LayerDeleted( size_t ) ),
    // to: 
    this,
    SLOT( OnLayerDeleted( size_t ) )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( OrderAboutToBeChanged() ),
    // to:
    this,
    SIGNAL( layoutAboutToBeChanged() )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( OrderChanged() ),
    // to:
    this,
    SIGNAL( layoutChanged() )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( ReferenceChanged( size_t ) ),
    // to:
    this,
    SLOT( OnReferenceChanged( size_t ) )
  );

  QObject::connect(
    m_StackedLayerModel,
    SIGNAL( PixelInfoChanged( const QPoint &, const PointType &, const PixelInfo::Vector & ) ),
    // to:
    this,
    SLOT( OnPixelInfoChanged( const QPoint &, const PointType &, const PixelInfo::Vector & ) )
  );

    QObject::connect(
      m_StackedLayerModel,
      SIGNAL( ResolutionsChanged( const PixelInfo::Vector & ) ),
      // to:
      this,
      SLOT( OnResolutionsChanged( const PixelInfo::Vector & ) )
    );

  for( StackedLayerModel::ConstIterator it( m_StackedLayerModel->Begin() );
       it!=m_StackedLayerModel->End();
       ++it )
    Connect( it->second );

  // emit layoutChanged();
}

/*****************************************************************************/
/* QAbstractItemModel overloads                                              */
/*****************************************************************************/
int
LayerStackItemModel
::columnCount( const QModelIndex & ) const
{
  // qDebug() << this << "::columnCount(" << parent << ")";

  return COLUMN_COUNT;
}

/*****************************************************************************/
QVariant
LayerStackItemModel
::data( const QModelIndex & idx, int role ) const
{
  // qDebug() << this << "::data(" << idx << "," << role << ")";

  // Get layer.
  assert( m_StackedLayerModel!=NULL );

  assert( idx.isValid() );
  assert( !idx.parent().isValid() );
  assert( idx.internalPointer()!=NULL );

  const AbstractLayerModel * layer =
    static_cast< const AbstractLayerModel * >( idx.internalPointer() );

  assert( layer!=NULL );

  // Return data given role.
  switch( role )
    {
    case Qt::CheckStateRole:
      if( idx.column()!=COLUMN_NAME )
        return QVariant();
      else
        {
        assert( layer==dynamic_cast< const VisibleInterface * >( layer ) );

        const VisibleInterface * interface =
          dynamic_cast< const VisibleInterface * >( layer );

        assert( interface!=NULL );

        return
          interface->IsVisible()
          ? Qt::Checked
          : Qt::Unchecked;
        }
      break;

    case Qt::EditRole:
    case Qt::DisplayRole:
      switch( idx.column() )
        {
        case COLUMN_PROJ:
	  return FromStdString( layer->GetAuthorityCode( true ) );
	  break;

	case COLUMN_RESOLUTION:
	{
	const PixelInfo::Vector & pixels = m_StackedLayerModel->PixelInfos();

	assert( idx.row()>=0 );

	if( idx.row()>=0 &&
	    static_cast< size_t >( idx.row() )<pixels.size() &&
	    pixels[ idx.row() ].m_HasResolution )
	  return
	    static_cast< qlonglong >( pixels[ idx.row() ].m_Resolution );
	else
	  return QVariant();
	}
	break;

        case COLUMN_NAME:
          return layer->GetName();
          break;

	case COLUMN_EFFECT:
          if( layer->inherits(
                VectorImageModel::staticMetaObject.className() ) )
            {
            const VectorImageModel * vectorImageModel =
              qobject_cast< const VectorImageModel * >( layer );
            assert( vectorImageModel!=NULL );

	    return vectorImageModel->GetSettings().GetEffectName();
            }
          else
            {
            qDebug() << "Unhandled AbstractLayerModel subclass.";
            }
	  break;

	case COLUMN_I:
	case COLUMN_J:
	{
	const PixelInfo::Vector & pixels = m_StackedLayerModel->PixelInfos();

	assert( idx.row()>=0 );

	if( idx.row()>=0 &&
	    static_cast< size_t >( idx.row() )<pixels.size() &&
	    pixels[ idx.row() ].m_HasIndex )
	  return
	    static_cast< qlonglong >( pixels[ idx.row() ].m_Index[ idx.column() - COLUMN_I ] );
	else
	  return QVariant();
	}
	break;

	case COLUMN_R:
	case COLUMN_G:
	case COLUMN_B:
	{
	const PixelInfo::Vector & pixels = m_StackedLayerModel->PixelInfos();

	assert( idx.row()>=0 );

	if( idx.row()>=0 &&
	    static_cast< size_t >( idx.row() )<pixels.size() &&
	    pixels[ idx.row() ].m_HasPixel )
	  return
	    pixels[ idx.row() ].m_Pixel[ idx.column() - COLUMN_R ];
	}
	break;

	case COLUMN_X:
	case COLUMN_Y:
	{
	const PixelInfo::Vector & pixels = m_StackedLayerModel->PixelInfos();

	assert( idx.row()>=0 );

	if( idx.row()>=0 &&
	    static_cast< size_t >( idx.row() )<pixels.size() &&
	    pixels[ idx.row() ].m_HasPoint )
	  return
	    pixels[ idx.row() ].m_Point[ idx.column() - COLUMN_X ];
	}
	  break;

	default:
	  break;
        }
      break;
    
    case Qt::FontRole:
      assert( idx.row()>=0 );

      if( static_cast< StackedLayerModel::SizeType >( idx.row() )==
	  m_StackedLayerModel->GetReferenceIndex() )
	{
	QFont font;

	font.setBold( true );

	return font;
	}
      break;

    case Qt::ToolTipRole:
      switch( idx.column() )
	{
	case COLUMN_NAME:
	  if( layer->inherits( VectorImageModel::staticMetaObject.className() ) )
	    {
            const VectorImageModel * vectorImageModel =
              qobject_cast< const VectorImageModel * >( layer );
            assert( vectorImageModel!=NULL );

            // qDebug() << "filename:" << vectorImageModel->GetFilename();

            return vectorImageModel->GetFilename();
	    }
	  break;
	}
      break;

    default:
      break;
    }

  return QVariant();
}

/*****************************************************************************/
bool
LayerStackItemModel
::dropMimeData( const QMimeData * dat,
                Qt::DropAction action,
                int row,
                int column,
                const QModelIndex & p )
{
  // qDebug()
  //   << this
  //   << "::dropMimeData("
  //   << dat << "," << action << "," << row << "," << column << "," << parent
  //   << ")";

  // qDebug() << "QMimeData::formats():" << dat->formats();

  return QAbstractItemModel::dropMimeData( dat, action, row, column, p );
}

/*****************************************************************************/
Qt::ItemFlags
LayerStackItemModel
::flags( const QModelIndex & idx ) const
{
  if( !idx.isValid() )
    return QAbstractItemModel::flags( idx );

  Qt::ItemFlags iflags =
    QAbstractItemModel::flags( idx )
    // | Qt::ItemIsDragEnabled
    // | Qt::ItemIsDropEnabled
    ;

  if( idx.column()==COLUMN_NAME )
    iflags |=
        Qt::ItemIsUserCheckable
      | Qt::ItemIsEditable
      | Qt::ItemIsDragEnabled;

  return iflags;
}

/*****************************************************************************/
bool
LayerStackItemModel
::hasChildren( const QModelIndex & idx ) const
{
  return !idx.isValid();
}

/*****************************************************************************/
QVariant
LayerStackItemModel
::headerData( int section,
              Qt::Orientation /**orientation*/,
              int role ) const
{
  // qDebug()
  //   << this << "::headerData("
  //   << section << "," << orientation << "," << role
  //   << ")";

  // assert( orientation==Qt::Horizontal );

  switch( role )
    {
    case Qt::DisplayRole:
      assert( section>=0 && section<COLUMN_COUNT );
      return tr( HEADERS[ section ] );
      break;

    default:
      break;
    }

  /*
  switch( orientation )
    {
    case Qt::Horizontal:
      switch( role )
        {
        case Qt::DisplayRole:
          assert( section>=0 && section<COLUMN_COUNT );
          return tr( HEADERS[ section ] );
          break;
        default:
          break;
        }
      break;

    case Qt::Vertical:
      break;

    default:
      assert( false && "Unhandled Qt::Orientation value." );
      break;
    }
  */

  return QVariant();
}

/*****************************************************************************/
QModelIndex
LayerStackItemModel
::index( int row,
         int column,
         const QModelIndex & p ) const
{
  // qDebug()
  //   << this << "::index(" << row << "," << column << "," << parent << ")";

  if (m_StackedLayerModel == NULL)
    return QModelIndex();

  // qDebug()
  //   << "index:" << row << "," << column << "," << m_StackedLayerModel->At( row );

  assert( row>=0 && column>=0 );

#if 1
  AbstractLayerModel * layer = m_StackedLayerModel->At( row );

  if( layer==NULL || p.isValid() )
    return QModelIndex();
#endif

  return
    createIndex(
      row,
      column,
      p.isValid()
      ? NULL
      : m_StackedLayerModel->At( row )
    );
}

/*****************************************************************************/
bool
LayerStackItemModel
::insertRows( int row, int count, const QModelIndex & p )
{
  assert( !p.isValid() );
  assert( count==1 );

  if( p.isValid() || count!=1 )
    return false;

  beginInsertRows( p, row, row + count - 1 );
  {
  // StackedLayerModel has already been grown.

  // TODO: Allocate additional row data here.
  }
  endInsertRows();

  // emit dataChanged( index( row, 0 ), index( last, columnCount() - 1 ) );

  return true;
}

/*****************************************************************************/
QMimeData *
LayerStackItemModel
::mimeData( const QModelIndexList & indexes ) const
{
  QMimeData * mData = QAbstractItemModel::mimeData( indexes );
  assert( mData!=NULL );

  typedef QList< QUrl > UrlList;

  UrlList urls;

  foreach( const QModelIndex & idx, indexes )
    if( idx.isValid() )
      {
      assert( idx.internalPointer()!=NULL );

      AbstractLayerModel * layer =
	static_cast< AbstractLayerModel * >( idx.internalPointer() );

      FilenameInterface * interface =
	dynamic_cast< FilenameInterface * >( layer );

      urls << QUrl::fromLocalFile( interface->GetFilename() );
      }

  mData->setUrls( urls );

  // qDebug() << this << "mime-data:" << mData;

  return mData;
}

/*****************************************************************************/
QStringList
LayerStackItemModel
::mimeTypes() const
{
  QStringList mTypes( QAbstractItemModel::mimeTypes() );

  mTypes << "text/uri-list";

  // qDebug() << this << "mime-types:" << mTypes;

  return mTypes;
}

/*****************************************************************************/
QModelIndex
LayerStackItemModel
::parent( const QModelIndex & ) const
{
  // qDebug() << this << "::parent(" << index << ")";

  return QModelIndex();
}

/*****************************************************************************/
bool
LayerStackItemModel
::removeRows( int row, int count, const QModelIndex & p )
{
  assert( !p.isValid() );
  assert( count==1 );

  if( p.isValid() || count!=1 )
    return false;

  beginRemoveRows( p, row, row + count - 1 );
  {
  // StackedLayerModel need to be shortened now
  emit LayerDeletingModel(row);
  // TODO: Release additional row data here.
  }

  endRemoveRows();

  return true;
}

/*****************************************************************************/
int
LayerStackItemModel
::rowCount( const QModelIndex & p ) const
{
  // qDebug() << this << "::rowCount(" << p << ")";

  // qDebug() << "row-count:" <<
  //   ( ( m_StackedLayerModel==NULL || p.isValid() )
  //     ? 0
  //     : m_StackedLayerModel->GetCount()
  //   );

  return
    ( m_StackedLayerModel==NULL || p.isValid() )
    ? 0
    : m_StackedLayerModel->GetCount();
}

/*****************************************************************************/
bool
LayerStackItemModel
::setData( const QModelIndex & idx,
           const QVariant & value,
           int role )
{
  // qDebug()
  //   << this << "::setData(" << idx << "," << value << "," << role << ")";

  if( idx.column()==COLUMN_NAME )
    {
    // qDebug() << idx.row() << "check-state:" << value;

    assert( !idx.parent().isValid() );
    assert( idx.internalPointer()!=NULL );

    assert( idx.internalPointer()!=NULL );

    AbstractLayerModel * layer =
      static_cast< AbstractLayerModel * >( idx.internalPointer() );

    assert( layer!=NULL );
    assert( layer==dynamic_cast< VisibleInterface * >( layer ) );
    VisibleInterface * interface = dynamic_cast< VisibleInterface * >( layer );
    assert( interface!=NULL );

    switch( role )
      {
      case Qt::EditRole:
        {
        QString strValue = value.toString();
        if( !strValue.isEmpty() )
          {
          layer->SetName( strValue );
          emit dataChanged( idx, idx );
          return true;
          }
        }
        break;
      case Qt::CheckStateRole:
        switch( value.toInt() )
          {
          case Qt::Checked:
            interface->SetVisible( true );
            break;

          case Qt::Unchecked:
            interface->SetVisible( false );
            break;

          default:
            assert( false && "Unhandled Qt::CheckedState value." );
            break;
          }
        emit dataChanged( idx, idx );
        return true;
        break;

      default:
        break;
      }
    }
  return false;
}

/*****************************************************************************/
Qt::DropActions
LayerStackItemModel
::supportedDropActions() const
{
  // qDebug() << this << "::supportedDropActions()";

  // return Qt::MoveAction;
  return QAbstractItemModel::supportedDropActions();
}

/*****************************************************************************/
/* SLOTS                                                                     */
/*****************************************************************************/
// void
// LayerStackItemModel
// ::OnContentAboutToBeChanged()
// {
//   qDebug() << this << "::OnAboutContentChanged()";

//   emit layoutAboutToBeChanged();
// }

/*****************************************************************************/
// void
// LayerStackItemModel
// ::OnContentChanged()
// {
//   qDebug() << this << "::OnContentChanged()";

//   // assert( m_StackedLayerModel!=NULL );

//   // beginResetModel();
//   // {
//   // }
//   // endResetModel();

//   // if( m_StackedLayerModel->GetCount()>0 )
//   //   emit dataChanged(
//   //     index( 0, 0 ),
//   //     index( m_StackedLayerModel->GetCount() - 1, COLUMN_COUNT -1 )
//   //   );

//   emit layoutChanged();
// }

/*****************************************************************************/
void
LayerStackItemModel
::OnImageSettingsUpdated( AbstractImageModel * image )
{
  // qDebug() << this << "::OnImageSettingsUpdated(" << image << ")";

  assert( m_StackedLayerModel!=NULL );

  StackedLayerModel::SizeType row = m_StackedLayerModel->IndexOf( image );

  assert( row!=StackedLayerModel::NIL_INDEX );

  emit dataChanged(
    createIndex( row, LayerStackItemModel::COLUMN_EFFECT, image ),
    createIndex( row, LayerStackItemModel::COLUMN_EFFECT, image )
  );
}

/*****************************************************************************/
void
LayerStackItemModel
::OnLayerAboutToBeDeleted( size_t idx )
{
  assert( m_StackedLayerModel!=NULL );

  Disconnect( m_StackedLayerModel->At( idx ) );
}

/*****************************************************************************/
void
LayerStackItemModel
::OnLayerAdded( size_t idx )
{
  if( !insertRow( idx ) )
    {
    assert( false && "QAbstractItemModel::insertRow() failed!" );
    return;
    }

  assert( m_StackedLayerModel!=NULL );

  Connect( m_StackedLayerModel->At( idx ) );
}

/*****************************************************************************/
void
LayerStackItemModel
::OnLayerDeleted( size_t idx )
{
#ifdef OTB_DEBUG
  bool isRowRemoved =
#endif

  removeRow( idx );

#ifdef OTB_DEBUG
  assert( isRowRemoved && "QAbstractItemModel::removeRow() failed!" );
#endif
}

/*****************************************************************************/
void
LayerStackItemModel
::OnLayerVisibilityChanged( AbstractLayerModel * layer, bool )
{
  // qDebug() << this << "::OnLayerVisibilityChanged(" << layer << "," << isVisible << ")"; 

  assert( m_StackedLayerModel!=NULL );
  assert( m_StackedLayerModel->IndexOf( layer )!=StackedLayerModel::NIL_INDEX );

  QModelIndex idx(
    createIndex(
      m_StackedLayerModel->IndexOf( layer ),
      COLUMN_NAME,
      layer
    )
  );

  emit dataChanged( idx, idx );
}

/*****************************************************************************/
void
LayerStackItemModel
::OnPixelInfoChanged( const QPoint &, const PointType &, const PixelInfo::Vector & pixels )
{
  if( pixels.empty() )
    return;

  emit dataChanged(
    index( 0, COLUMN_I ),
    index( pixels.size() - 1, COLUMN_COUNT - 1 )
  );
}

/*****************************************************************************/
// void
// LayerStackItemModel
// ::OnModelAboutToBeReset()
// {
//   qDebug() << this << "::OnModelAboutToBeReset()";
// }

/*****************************************************************************/
// void
// LayerStackItemModel
// ::OnModelReset()
// {
//   qDebug() << this << "::OnModelReset()";
// }

/*****************************************************************************/
void
LayerStackItemModel
::OnReferenceChanged( size_t idx )
{
  // qDebug() << this << "::OnReferenceChanged(" << idx << ")";

  assert( m_StackedLayerModel!=NULL );

  AbstractLayerModel * layer = m_StackedLayerModel->GetReference();

  assert( LayerStackItemModel::COLUMN_COUNT>0 );

  emit dataChanged(
    createIndex( idx, 0, layer ),
    createIndex( idx, LayerStackItemModel::COLUMN_COUNT - 1, layer )
  );
}

/*****************************************************************************/
void
LayerStackItemModel
::OnResolutionsChanged( const PixelInfo::Vector & pixels )
{
  if( pixels.empty() )
    return;

  emit dataChanged(
    index( 0, COLUMN_RESOLUTION ),
    index( pixels.size() - 1, COLUMN_RESOLUTION )
  );
}

/*****************************************************************************/

} // end namespace 'mvd'
