/*
 * 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 "mvdStackedLayerModel.h"


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

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

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

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

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

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

namespace mvd
{
/*
  TRANSLATOR mvd::StackedLayerModel

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

  Context comment for translator.
*/


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

namespace
{
} // end of anonymous namespace.


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

StackedLayerModel::SizeType
StackedLayerModel::m_LayerCount = 0;

const StackedLayerModel::KeyType
StackedLayerModel::NIL_KEY;

const StackedLayerModel::SizeType
StackedLayerModel::NIL_INDEX = -1;

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

/*******************************************************************************/
StackedLayerModel
::StackedLayerModel( QObject* p ) :
  AbstractModel( p ),
  m_LayerModels(),
  m_Keys(),
  m_Current( StackedLayerModel::NIL_INDEX ),
  m_Reference( StackedLayerModel::NIL_INDEX )
{
}

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

/*****************************************************************************/
StackedLayerModel::KeyType
StackedLayerModel
::Add( AbstractLayerModel * model )
{
  return Insert( model, StackedLayerModel::NIL_INDEX );
}

/*****************************************************************************/
void
StackedLayerModel
::Clear()
{
  bool emitSignal0 = !IsEmpty();
  bool emitSignal1 = !IsEmpty() && m_Current<GetCount();
  bool emitSignal2 = !IsEmpty() && m_Reference<GetCount();


  if( emitSignal0 )
    emit ContentAboutToBeReset();

  //
  // Clear current.
  if( emitSignal1 )
    {
    emit CurrentAboutToBeChanged( StackedLayerModel::NIL_INDEX );
    emit AboutToChangeSelectedLayerModel( KeyType() );
    }

  m_Current = StackedLayerModel::NIL_INDEX;

  if( emitSignal1 )
    {
    emit CurrentChanged( m_Current );
    emit SelectedLayerModelChanged( KeyType() );
    }

  //
  // Clear reference.
  if( emitSignal2 )
    emit ReferenceAboutToBeChanged( StackedLayerModel::NIL_INDEX );

  m_Reference = StackedLayerModel::NIL_INDEX;

  if( emitSignal2 )
    emit ReferenceChanged( m_Reference );

  //
  // Clear content.
  ClearPixelInfos();

  for( LayerModelMap::iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    if( it->second->parent()==this )
      {
      delete it->second;
      it->second = NULL;
      }
    }

  m_LayerModels.clear();
  m_Keys.clear();

  if( emitSignal0 )
    emit ContentReset();
}

/*****************************************************************************/
bool
StackedLayerModel
::Contains( const AbstractLayerModel * layerModel ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    if( it->second==layerModel )
      return true;

  return false;
}

/*****************************************************************************/
void
StackedLayerModel
::CountSRT( SizeType & unk,
	    SizeType & crt,
	    SizeType & geo,
	    SizeType & ssr ) const
{
  for( LayerModelMap::const_iterator it( m_LayerModels.begin() );
       it!=m_LayerModels.end();
       ++it )
    {
    assert( it->second!=NULL );

    switch( it->second->GetSpatialReferenceType() )
      {
      case SRT_UNKNOWN:
	++ unk;
	break;

      case SRT_CARTO:
	++ crt;
	break;

      case SRT_GEO:
	++ geo;
	break;

      case SRT_SENSOR:
	++ ssr;
	break;
      }
    }
}

/*****************************************************************************/
void
StackedLayerModel
::Deleting( unsigned int index )
{
    // qDebug() << this << "::Delete(" << index << ")";


  //
  // Check content.
  if( IsEmpty() )
    return;

  if( index>=GetCount() )
    return;

  assert( !m_Keys[ index ].empty() );

  //
  // Find item.
  LayerModelMap::iterator it(
    m_LayerModels.find( m_Keys[ index ] )
  );

  assert( it!=m_LayerModels.end() );

  //
  // Check if signals have to be emitted.
  bool emitCurrentChanged = m_Current<GetCount() && index<=m_Current;
  bool emitReferenceChanged = m_Reference<GetCount() && index<=m_Reference;

  //
  // Remember new current index.
  SizeType current =
    index>m_Current
    ? m_Current
    : ( m_Current>0
        ? m_Current - 1
        : GetCount()>1 ? 0 : StackedLayerModel::NIL_INDEX );

  //
  // Emit signals.
  emit ContentAboutToBeChanged();
  emit LayerAboutToBeDeleted( index );

  //
  // Clear satellite date.
  ClearPixelInfos();

  //
  // Remove layer-model.
  AbstractLayerModel * layer = it->second;

  // Disconnect the renaming signals
  QObject::disconnect(
    layer,
    SIGNAL( NameChanged() ),
    // from:
    this,
    SIGNAL( LayerRenamed() )
  );

  m_LayerModels.erase( it );

  m_Keys.erase( m_Keys.begin() + index );

  it = m_LayerModels.end();

  //
  // Update pointer to current.
  if( emitCurrentChanged )
    SetCurrent( current, true );

  //
  // Update reference pointer.
  if( emitReferenceChanged )
    SetReference(
      index>m_Reference
      ? m_Reference
      : ( m_Reference > 0
    ? m_Reference - 1
          : GetCount() > 0 ? 0 : StackedLayerModel::NIL_INDEX ),
      true
    );

  //
  // Eventually delete layer.
  if( layer->parent()==this )
    {
    delete layer;
    layer = NULL;
    }
}

void
StackedLayerModel
::Delete( SizeType index )
{
  // the work of deleting model is now done in the above function
  // Deleting(unsigned int index)
  // Emit signals.
  emit LayerDeleted( index );
  emit ContentChanged();
}

/*****************************************************************************/
void
StackedLayerModel
::EndEditResolutions()
{
  // qDebug() << this << "::EndEditResolutions()";

  emit ResolutionsChanged( m_PixelInfos );
}

/*******************************************************************************/
std::string
StackedLayerModel
::GenerateKey( AbstractLayerModel * otbUseInDebug( layerModel ) )
{
  std::ostringstream oss;

  oss << "Layer #" << m_LayerCount++;

#ifdef OTB_DEBUG 
  oss << " (" << layerModel->metaObject()->className()
      << "@" << std::hex << layerModel << ")";
#endif

  return oss.str();
}

/*****************************************************************************/
StackedLayerModel::KeyType
StackedLayerModel
::Insert( AbstractLayerModel * model, SizeType index )
{
  // qDebug() << this << "::Insert(" << model << "," << index << ")";

  //
  // Check given model.
  assert( model!=NULL );

  if( model==NULL )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Cannot insert NULL AbstractLayerModel." )
        )
      );
    }

  //
  // Generate key for new layer.
  std::string key( StackedLayerModel::GenerateKey( model ) );
  assert( !key.empty() );

  if( key.empty() )
    {
    throw
      std::runtime_error(
        ToStdString(
          tr( "Failed to generate string key for '%1'." )
          .arg( model->metaObject()->className() )
        )
      );
    }

  //
  // Clamp index. If out of bounds, insert model at the end of stack.
  if( index>GetCount() )
    index = GetCount();

  //
  // Check if signals have to be emitted.
  bool emitCurrentChanged = m_Current<GetCount() && index<=m_Current;
  bool emitReferenceChanged = m_Reference<GetCount() && index<=m_Reference;

  //
  // Emit signals.
  emit ContentAboutToBeChanged();

  //
  // Clear satellite date.
  ClearPixelInfos();

  //
  // Insert model.
  m_LayerModels.insert( LayerModelMap::value_type( key, model ) );
  m_Keys.insert( m_Keys.begin() + index, key );

  // Connect the renaming signals
  QObject::connect(
    model,
    SIGNAL( NameChanged() ),
    // to:
    this,
    SIGNAL( LayerRenamed() )
  );

  //
  // Update pointer to current.
  if( emitCurrentChanged )
    SetCurrent( m_Current + 1, true );

  //
  // Update reference pointer.
  if( emitReferenceChanged )
    SetReference( m_Reference + 1, true );

  //
  // Emit signals.
  emit LayerAdded( index );
  emit ContentChanged();

  //
  // Return generated key.
  return key;
}

/*****************************************************************************/
void
StackedLayerModel
::LowerLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType next = Next( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + next )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current
  emit CurrentAboutToBeChanged( next );
  {
  m_Current = next;
  }
  emit CurrentChanged( next );

  // WARNING: This may be buggy if index!=m_Reference
  SetReference(
    m_Reference==next
    ? index
    : ( m_Reference==index
        ? next
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveTo( SizeType index, SizeType position )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );
  assert( position<GetCount() );

  if( index==position )
    return;

  //
  // Move element.
  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  KeyType key( m_Keys[ index ] );

  m_Keys.erase( m_Keys.begin() + index );
  m_Keys.insert( m_Keys.begin() + position, key );
  }
  emit OrderChanged();

  //
  // Compute new current element.
  SizeType current( m_Current );

  if( index==m_Current )
    current = position;

  if( index>m_Current && position<=m_Current )
    ++ current;

  else if( index<m_Current && position>=m_Current )
    -- current;

  //
  // Signal new current element.
  SetCurrent( current );

  //
  // Compute new reference element.
  SizeType reference( m_Reference );

  if( index==m_Reference )
    reference = position;

  if( index>m_Reference && position<=m_Reference )
    ++ reference;

  else if( index<m_Reference && position>=m_Reference )
    -- reference;

  SetReference( reference );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToBottom( SizeType index )
{
  MoveTo( index, m_Keys.size() - 1 );
}

/*****************************************************************************/
void
StackedLayerModel
::MoveToTop( SizeType index )
{
  MoveTo( index, 0 );
}

/*****************************************************************************/
void
StackedLayerModel
::RaiseLayer( SizeType index )
{
  assert( GetCount()>1 );
  assert( index<GetCount() );

  SizeType prev = Prev( index );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::swap(
    *( m_Keys.begin() + index ),
    *( m_Keys.begin() + prev )
  );
  }
  emit OrderChanged();

  // WARNING: This may be buggy if index!=m_Current.
  emit CurrentAboutToBeChanged( prev );
  {
  m_Current = prev;
  }
  emit CurrentChanged( prev );

  // WARNING: This may be buggy if index!=m_Reference.
  SetReference(
    m_Reference==prev
    ? index
    : ( m_Reference==index
        ? prev
        : m_Reference )
  );
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerUp( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.begin(), m_Keys.begin() + index, m_Keys.end() );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
      m_Current = current;
    }
    emit CurrentChanged( m_Current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
      m_Reference = reference;
    }
    emit ReferenceChanged( m_Reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::RotateLayerDown( SizeType index )
{
  if( GetCount()<2 )
    return;

  if( index>=GetCount() )
    return;

  KeyType currentKey( GetKey( m_Current ) );
  KeyType referenceKey( GetKey( m_Reference ) );

  emit OrderAboutToBeChanged();
  {
  ClearPixelInfos();

  std::rotate( m_Keys.rbegin(), m_Keys.rbegin() + index, m_Keys.rend()  );
  }
  emit OrderChanged();

  if( !currentKey.empty() )
    {
    SizeType current = FindKey( currentKey );

    assert( current!=StackedLayerModel::NIL_INDEX );

    emit CurrentAboutToBeChanged( current );
    {
    m_Current = current;
    }
    emit CurrentChanged( current );
    }

  if( !referenceKey.empty() )
    {
    SizeType reference = FindKey( referenceKey );

    assert( reference!=StackedLayerModel::NIL_INDEX );

    emit ReferenceAboutToBeChanged( reference );
    {
    m_Reference = reference;
    }
    emit ReferenceChanged( reference );
    }

  // qDebug() << "current:" << index;
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const AbstractLayerModel * layerModel )
{
  SetCurrent( KeyOf( layerModel ) );
}

/*****************************************************************************/
void
StackedLayerModel
::SetCurrent( const KeyType & key )
{
  if( key==GetCurrentKey() )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetCurrent( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetCurrent( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const KeyType & key )
{
  if( key==GetKey( m_Reference ) )
    return;

  if( key==StackedLayerModel::NIL_KEY )
    {
    SetReference( StackedLayerModel::NIL_INDEX );

    return;
    }

  for( SizeType i=0; i<m_Keys.size(); ++i )
    if( m_Keys[ i ]==key )
      {
      SetReference( i );
      return;
      }
}

/*****************************************************************************/
void
StackedLayerModel
::SetReference( const AbstractLayerModel * layerModel )
{
  SetReference( KeyOf( layerModel ) );
}

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

} // end namespace 'mvd'
