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

#ifndef otbChannelSelectorFunctor_h
#define otbChannelSelectorFunctor_h
#include <cassert>

#include "itkVariableLengthVector.h"
#include "itkRGBPixel.h"
#include "itkRGBAPixel.h"
#include "itkObject.h"
#include "itkObjectFactory.h"

namespace otb
{
namespace Function
{
/** \class ChannelSelectorFunctor
* \brief Base class for pixel representation functions.
*
*  \ingroup Visualization
*
* \sa AmplitudeFunctor PhaseFunctor
 *
 * \ingroup OTBCommon
*/
template <class TInputPixel>
class ChannelSelectorFunctor : public itk::Object
{
public:
  /** Standard class typedefs */
  typedef ChannelSelectorFunctor        Self;
  typedef itk::Object                   Superclass;
  typedef itk::SmartPointer<Self>       Pointer;
  typedef itk::SmartPointer<const Self> ConstPointer;
  /** Method for creation through the object factory */
  itkNewMacro(Self);

  /** Runtime information */
  itkTypeMacro(ChannelSelectorFunctor, itk::Object);

  typedef TInputPixel                                       PixelType;
  typedef typename itk::NumericTraits<PixelType>::ValueType ScalarType;
  typedef itk::VariableLengthVector<ScalarType>             VectorPixelType;
  typedef itk::RGBPixel<ScalarType>                         RGBPixelType;
  typedef itk::RGBAPixel<ScalarType>                        RGBAPixelType;

  typedef VectorPixelType OutputPixelType;

  typedef std::vector<unsigned int> ChannelListType;

  const char* GetDescription() const
  {
    return "Channel Selection";
  }

  virtual OutputPixelType operator()(const VectorPixelType& inPixel) const
  {
    //           otbMsgDevMacro(<<"Channel list "<< m_ChannelList[0]);
    OutputPixelType outPixel;
    outPixel.SetSize(m_ChannelList.size());
    for (unsigned int i = 0; i < m_ChannelList.size(); ++i)
    {
      // assert as the verification should be done outside and only
      // once for the image, not for every pixel, when we reach this point
      // the m_ChannelList MUST be valid
      assert(m_ChannelList[i] < inPixel.Size());
      outPixel[i] = inPixel[m_ChannelList[i]];
    }
    return outPixel;
  }

  virtual OutputPixelType operator()(ScalarType inPixel) const
  {
    OutputPixelType outPixel;
    outPixel.SetSize(1);
    for (unsigned int i = 0; i < m_ChannelList.size(); ++i)
    {
      assert(m_ChannelList[i] < 1);
      outPixel[0] = inPixel;
    }
    return outPixel;
  }

  virtual OutputPixelType operator()(const RGBPixelType& inPixel) const
  {
    OutputPixelType outPixel;
    outPixel.SetSize(m_ChannelList.size());
    for (unsigned int i = 0; i < m_ChannelList.size(); ++i)
    {
      assert(m_ChannelList[i] < 3);
      outPixel[i] = inPixel[m_ChannelList[i]];
    }
    return outPixel;
  }

  virtual OutputPixelType operator()(const RGBAPixelType& inPixel) const
  {
    OutputPixelType outPixel;
    outPixel.SetSize(m_ChannelList.size());
    for (unsigned int i = 0; i < m_ChannelList.size(); ++i)
    {
      assert(m_ChannelList[i] < 4);
      outPixel[i] = inPixel[m_ChannelList[i]];
    }
    return outPixel;
  }

  virtual unsigned int GetOutputSize() const
  {
    return m_ChannelList.size();
  }

  virtual std::vector<unsigned int> GetChannelList() const
  {
    return m_ChannelList;
  }

  virtual void SetChannelList(std::vector<unsigned int> channels)
  {
    m_ChannelList          = channels;
    usingDefaultParameters = false;
  }

  virtual void SetChannelIndex(unsigned int channelPosition, unsigned int channel)
  {
    if (m_ChannelList.size() < channelPosition + 1)
    {
      m_ChannelList.resize(channelPosition + 1, 0);
    }
    m_ChannelList[channelPosition] = channel;
    usingDefaultParameters         = false;
  }

  virtual unsigned int GetChannelIndex(unsigned int channelPosition) const
  {
    if (channelPosition >= m_ChannelList.size())
    {
      itkExceptionMacro(<< "Can't get channel " << channelPosition << ", there is only " << m_ChannelList.size() << " element in the list");
    }
    return m_ChannelList[channelPosition];
  }

  /** Only for backward compatibility but should not be used*/
  virtual void SetAllChannels(unsigned int channel)
  {
    if (m_ChannelList.size() < 3)
    {
      m_ChannelList.resize(3, 0);
    }
    m_ChannelList[0]       = channel;
    m_ChannelList[1]       = channel;
    m_ChannelList[2]       = channel;
    usingDefaultParameters = false;
  }
  virtual void SetRedChannelIndex(unsigned int channel)
  {
    if (m_ChannelList.size() < 1)
    {
      m_ChannelList.resize(3, 0);
    }
    m_ChannelList[0]       = channel;
    usingDefaultParameters = false;
  }

  virtual void SetGreenChannelIndex(unsigned int channel)
  {
    if (m_ChannelList.size() < 2)
    {
      m_ChannelList.resize(3, 0);
    }
    m_ChannelList[1]       = channel;
    usingDefaultParameters = false;
  }

  virtual void SetBlueChannelIndex(unsigned int channel)
  {
    if (m_ChannelList.size() < 3)
    {
      m_ChannelList.resize(3, 0);
    }
    m_ChannelList[2]       = channel;
    usingDefaultParameters = false;
  }

  virtual unsigned int GetRedChannelIndex() const
  {
    return m_ChannelList[0];
  }
  virtual unsigned int GetGreenChannelIndex() const
  {
    return m_ChannelList[1];
  }
  virtual unsigned int GetBlueChannelIndex() const
  {
    return m_ChannelList[2];
  }

  virtual bool IsUsingDefaultParameters()
  {
    return usingDefaultParameters;
  }

protected:
  /** Constructor */
  ChannelSelectorFunctor() : usingDefaultParameters(true)
  {
    if (std::string(typeid(PixelType).name()).find("RGBAPixel") != std::string::npos)
    {
      m_ChannelList.push_back(0);
      m_ChannelList.push_back(1);
      m_ChannelList.push_back(2);
      m_ChannelList.push_back(3);
    }
    else if (std::string(typeid(PixelType).name()).find("RGBPixel") != std::string::npos)
    {
      m_ChannelList.push_back(0);
      m_ChannelList.push_back(1);
      m_ChannelList.push_back(2);
    }
    else if (std::string(typeid(PixelType).name()).find("VariableLengthVector") != std::string::npos)
    {
      m_ChannelList.push_back(0);
    }
    else
    {
      m_ChannelList.push_back(0);
    }
  }

  /** Destructor */
  ~ChannelSelectorFunctor() override
  {
  }

private:
  ChannelListType m_ChannelList;
  bool            usingDefaultParameters;
};
}
}

#endif
