/*
 * 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.
 */

#ifndef mvdVectorImageSettings_h
#define mvdVectorImageSettings_h

//
// Configuration include.
//// Included at first position before any other ones.
#include "ConfigureMonteverdi.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 "mvdCore.h"
#include "mvdImageSettings.h"


/*****************************************************************************/
/* PRE-DECLARATION SECTION                                                   */

//
// External classes pre-declaration.
namespace
{
}

namespace mvd
{
//
// Internal classes pre-declaration.


/*****************************************************************************/
/* CLASS DEFINITION SECTION                                                  */

/**
 * \class VectorImageSettings
 *
 * \ingroup OTBMonteverdiCore
 *
 * \brief WIP.
 */
class OTBMonteverdiCore_EXPORT VectorImageSettings :
    public ImageSettings
{

  /*-[ PUBLIC SECTION ]------------------------------------------------------*/
//
// Public types and attributes.
public:
  /**
   */
  typedef UIntVector ChannelVector;

//
// Public methods.
public:
  /**
   * \brief Constructor.
   */
  VectorImageSettings();

  /**
   * \brief Copy constructor.
   *
   * \param other
   *
   * Copy settings POD content without the modified flag.
   */
  VectorImageSettings( const VectorImageSettings & other );

  /**
   * \brief Destructor.
   */
  ~VectorImageSettings() override;

  /**
   * \brief Assignment operator.
   *
   * \param other
   */
#if 0
  VectorImageSettings & operator = ( const VectorImageSettings & other );
#endif

  //
  // COLOR COMPOSITION.
  //

  /**
   */
  inline void SetRgbChannels( const ChannelVector& rgb );

  /**
   */
  inline const ChannelVector& GetRgbChannels() const;

  /**
   */
  inline
    void SetRgbChannel( ChannelVector::size_type i,
			const ChannelVector::value_type& channel );

  /**
   */
  inline
    const ChannelVector::value_type&
    GetRgbChannel( ChannelVector::size_type i ) const;

  /**
   * \return the band-index for the given component taking the
   * grayscale-mode activation state flag into account.
   */
  inline
    ChannelVector::value_type
    GetSmartChannel( RgbwChannel channel ) const;

  /**
   * \return the band-index for the given component not taking the
   * grayscale-mode activation state flag into account.
   */
  ChannelVector::value_type GetRgbwChannel( RgbwChannel channel ) const;

  /**
   * \return the channels band-index vector taking the
   * grayscale-mode activation state flag into account.
   */
  inline void GetSmartChannels( ChannelVector& channels ) const;

  //
  // COLOR DYNAMICS.
  //

  /**
   */
  inline void SetRgbDynamicsParams( const ParametersType& params );

  /**
   */
  inline const ParametersType& GetRgbDynamicsParams() const;

  /**
   */
  inline ParametersType GetSmartDynamicsParams() const;

  /**
   * Set low-intensity dynamics parameter for given channel. If
   * channel is RGBW_CHANNEL_WHITE, this method sets all RGB
   * channels.
   *
   * \param channel The RGB/W channel to set low-intensity dynamics for.
   * \param intensity low-intensity value.
   */
  void SetLowIntensity( RgbwChannel channel,
                        ParametersType::ValueType intensity );

  /**
   */
  ParametersType::ValueType GetLowIntensity( RgbwChannel channel ) const;

  /**
   * Set high-intensity dynamics parameter for given channel. If
   * channel is RGBW_CHANNEL_WHITE, this method sets all RGB
   * channels.
   *
   * \param channel The RGB/W channel to set high-intensity dynamics for.
   * \param intensity high-intensity value.
   */
  void SetHighIntensity( RgbwChannel channel,
                         ParametersType::ValueType intensity );

  /**
   */
  ParametersType::ValueType GetHighIntensity( RgbwChannel channel ) const;

  //
  // GRAYSCALE MODE.
  //

  /**
   * \brief Set/clear grayscale-mode activation-state flag for image.
   */
  inline void SetGrayscaleActivated( bool activated );

  /**
   * \return Grayscale-mode activation state flag.
   */
  inline bool IsGrayscaleActivated() const;

  /**
   * \brief Set white (gray) channel index.
   *
   * \param index The new index of white (gray) channel.
   */
  inline void SetGrayChannel( unsigned int );

  /**
   * \return White (gray) channel index.
   */
  inline unsigned int GetGrayChannel() const;

  /**
   */
  inline void SetGrayDynamicsParams( const ParametersType& params );

  /**
   */
  inline const ParametersType& GetGrayDynamicsParams() const;

  /**
   */
  inline void SetGamma( double gamma );

  /**
   */
  inline double GetGamma() const;


  /*-[ PROTECTED SECTION ]---------------------------------------------------*/

//
// Protected methods.
protected:

//
// Protected attributes.
protected:

  /*-[ PRIVATE SECTION ]-----------------------------------------------------*/

//
// Private methods.
private:

  /**
   */
  inline
    const ParametersType::ValueType& GetRgbDynamicsParam( CountType i ) const;

  /**
   */
  inline
    void SetRgbDynamicsParam( CountType i,
			      const ParametersType::ValueType& param );

  /**
   */
  inline
    const ParametersType::ValueType& GetGrayDynamicsParam( bool high ) const;

  /**
   */
  inline
    void SetGrayDynamicsParam( bool high,
			       const ParametersType::ValueType& param );


//
// Private attributes.
private:
  /**
   * \brief Color-composition setup (file-component to video
   * RGB-components).
   */
  ChannelVector m_RgbChannels;

  /**
   * \brief Color-dynamics parameters (\sa
   * HistogramModel::Quantile()).
   */
  ParametersType m_RgbDynamicsParams;

  /**
   * \brief Grayscale-mode activation state.
   */
  bool m_IsGrayscaleActivated;

  /**
   * \brief Grayscale-mode band-index.
   */
  ChannelVector::value_type m_GrayChannel;

  /**
   * \brief Color-dynamics parameters (\sa
   * HistogramModel::Quantile()).
   */
  ParametersType m_GrayDynamicsParams;

  /**
   */
  double m_Gamma;
};

} // end namespace 'mvd'.

/*****************************************************************************/
/* INLINE 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)

namespace mvd
{

/*****************************************************************************/
#if 0

inline
VectorImageSettings &
VectorImageSettings
::operator = ( const VectorImageSettings & other )
{
  if( &other==this )
    return *this;

  m_RgbChannels = other.m_RgbChannels;
  m_RgbDynamicsParams = other.m_RgbDynamicsParams;
  m_IsGrayscaleActivated = other.m_IsGrayscaleActivated;
  m_GrayChannel = other.m_GrayChannel;
  m_GrayDynamicsParams = other.m_GrayDynamicsParams;
  m_Gamma = other.m_Gamma;

  return *this;
}

#endif

/*****************************************************************************/
inline
void
VectorImageSettings
::SetRgbChannels( const ChannelVector& rgb )
{
  // qDebug() << "setRgbChannels()";

  assert( m_RgbChannels.empty() || m_RgbChannels.size()==rgb.size() );

  if( !m_RgbChannels.empty() &&
      std::equal( m_RgbChannels.begin(), m_RgbChannels.end(), rgb.begin() ) )
    return;

  m_RgbChannels = rgb;

  SetModified();
}

/*****************************************************************************/
inline
const VectorImageSettings::ChannelVector&
VectorImageSettings
::GetRgbChannels() const
{
  return m_RgbChannels;
}

/*****************************************************************************/
inline
void
VectorImageSettings
::GetSmartChannels( VectorImageSettings::ChannelVector& channels ) const
{
  if( IsGrayscaleActivated() )
    {
    channels.resize( m_RgbChannels.size() );
    std::fill( channels.begin(), channels.end(), m_GrayChannel );
    }
  else
    {
    channels = m_RgbChannels;
    }
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetRgbChannel( ChannelVector::size_type i,
		 const ChannelVector::value_type& channel )
{
  // qDebug() << "SetRgbChannel()";

  if( m_RgbChannels[ i ]==channel )
    return;

  SetModified();

  m_RgbChannels[ i ] = channel;
}

/*****************************************************************************/
inline
const VectorImageSettings::ChannelVector::value_type&
VectorImageSettings
::GetRgbChannel( ChannelVector::size_type i ) const
{
  return m_RgbChannels[ i ];
}

/*****************************************************************************/
inline
VectorImageSettings::ChannelVector::value_type
VectorImageSettings
::GetSmartChannel( RgbwChannel channel ) const
{
  return GetRgbwChannel(
    m_IsGrayscaleActivated
    ? RGBW_CHANNEL_WHITE
    : channel
  );

  /*
  if( m_IsGrayscaleActivated )
    {
    return m_GrayChannel;
    }
  else
    {
    return GetRgbwChannel( channel );
    }
  */
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetRgbDynamicsParams( const ParametersType& params )
{
  // qDebug() << "SetDynamicsParams()";

  assert( m_RgbDynamicsParams.size()==params.size() );

  if( std::equal( m_RgbDynamicsParams.begin(),
		  m_RgbDynamicsParams.end(),
		  params.begin() ) )
    return;

  m_RgbDynamicsParams = params;

  SetModified();
}

/*****************************************************************************/
inline
const ParametersType&
VectorImageSettings
::GetRgbDynamicsParams() const
{
  return m_RgbDynamicsParams;
}

/*****************************************************************************/
inline
ParametersType
VectorImageSettings
::GetSmartDynamicsParams() const
{
  return m_IsGrayscaleActivated ? m_GrayDynamicsParams : m_RgbDynamicsParams;
}

/*****************************************************************************/
inline
const ParametersType::ValueType&
VectorImageSettings
::GetRgbDynamicsParam( CountType i ) const
{
  return m_RgbDynamicsParams[ i ];
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetRgbDynamicsParam( CountType i,
		       const ParametersType::ValueType& param )
{
  // qDebug() << "SetDynamicsParam()";

  if( m_RgbDynamicsParams[ i ]==param )
    return;

  SetModified();

  m_RgbDynamicsParams[ i ] = param;
}

/*****************************************************************************/
inline
bool
VectorImageSettings
::IsGrayscaleActivated() const
{
  return m_IsGrayscaleActivated;
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetGrayscaleActivated( bool activated )
{
  m_IsGrayscaleActivated = activated;

  Effect effect = GetEffect();

  if(activated
     && effect == EFFECT_SPECTRAL_ANGLE)
    {
    SetEffect(EFFECT_NORMAL);
    }
  else if(!activated && effect >= EFFECT_LUT_LOCAL_JET)
    {
    SetEffect(EFFECT_NORMAL);
    }
  
  SetModified();
}

/*****************************************************************************/
inline
unsigned int
VectorImageSettings
::GetGrayChannel() const
{
  return m_GrayChannel;
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetGrayChannel( unsigned int index )
{
  m_GrayChannel = index;

  SetModified();
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetGrayDynamicsParams( const ParametersType& params )
{
  // qDebug() << "SetDynamicsParams()";

  assert( m_GrayDynamicsParams.size()==params.size() );

  if( std::equal( m_GrayDynamicsParams.begin(),
		  m_GrayDynamicsParams.end(),
		  params.begin() ) )
    return;

  m_GrayDynamicsParams = params;

  SetModified();
}

/*****************************************************************************/
inline
const ParametersType&
VectorImageSettings
::GetGrayDynamicsParams() const
{
  return m_GrayDynamicsParams;
}

/*****************************************************************************/
inline
const ParametersType::ValueType&
VectorImageSettings
::GetGrayDynamicsParam( bool high ) const
{
  return m_GrayDynamicsParams[ high ? 1 : 0 ];
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetGrayDynamicsParam( bool high,
			const ParametersType::ValueType& param )
{
  // qDebug() << "SetDynamicsParam()";

  CountType ofs = high ? 1 : 0;

  if( m_GrayDynamicsParams[ 0 * 2 + ofs ]==param )
    return;

  SetModified();

  // Indices are detailed for better readability since the compiler
  // will optimize constants.
  m_GrayDynamicsParams[ 0 * 2 + ofs ] = param;
  m_GrayDynamicsParams[ 1 * 2 + ofs ] = param;
  m_GrayDynamicsParams[ 2 * 2 + ofs ] = param;
}

/*****************************************************************************/
inline
void
VectorImageSettings
::SetGamma( double value )
{
  m_Gamma = value;

  SetModified();
}

/*****************************************************************************/
inline
double
VectorImageSettings
::GetGamma() const
{
  return m_Gamma;
}

} // end namespace 'mvd'

#endif // mvdVectorImageSettings_h
