/***************************************************************************
                qgsrastertransparency.h -  description
                       -------------------
begin                : Mon Nov 30 2007
copyright            : (C) 2007 by Peter J. Ersts
email                : ersts@amnh.org

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

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
#ifndef QGSRASTERTRANSPARENCY_H
#define QGSRASTERTRANSPARENCY_H

#include "qgis.h"
#include "qgis_core.h"
#include "qgis_sip.h"

#include <QList>
#include <QString>

using namespace Qt::StringLiterals;

class QDomDocument;
class QDomElement;

/**
 * \ingroup core
 * \brief Defines the list of pixel values to be considered as transparent or semi
 * transparent when rendering rasters.
 */
class CORE_EXPORT QgsRasterTransparency
{
  public:
    QgsRasterTransparency() = default;

    /**
     * \ingroup core
     * \brief Defines the transparency for a RGB pixel value.
     */
    struct TransparentThreeValuePixel
    {
        /**
      * Constructor for TransparentThreeValuePixel.
      * \param red red pixel value
      * \param green green pixel value
      * \param blue blue pixel value
      * \param opacity opacity for pixel, between 0 and 1.0
      * \param fuzzyToleranceRed (since QGIS 3.40) allows specifying a tolerance for the red color component, where the pixel's red component can deviate from values specified here by a maximum of this tolerance amount
      * \param fuzzyToleranceGreen (since QGIS 3.40) allows specifying a tolerance for the green color component, where the pixel's green component can deviate from values specified here by a maximum of this tolerance amount
      * \param fuzzyToleranceBlue (since QGIS 3.40) allows specifying a tolerance for the blue color component, where the pixel's blue component can deviate from values specified here by a maximum of this tolerance amount
      * \since QGIS 3.38
      */
        TransparentThreeValuePixel(
          double red = 0,
          double green = 0,
          double blue = 0,
          double opacity = 0,
          double fuzzyToleranceRed = 4 * std::numeric_limits<double>::epsilon(),
          double fuzzyToleranceGreen = 4 * std::numeric_limits<double>::epsilon(),
          double fuzzyToleranceBlue = 4 * std::numeric_limits<double>::epsilon()
        )
          : red( red )
          , green( green )
          , blue( blue )
          , opacity( opacity )
          , fuzzyToleranceRed( fuzzyToleranceRed )
          , fuzzyToleranceGreen( fuzzyToleranceGreen )
          , fuzzyToleranceBlue( fuzzyToleranceBlue )
        {}

        /**
       * Red pixel value.
       */
        double red;

        /**
      * Green pixel value.
      */
        double green;

        /**
       * Blue pixel value.
       */
        double blue;

        /**
      * Opacity for pixel, between 0 and 1.0.
      *
      * \since QGIS 3.38
      */
        double opacity = 0;

        /**
      * Fuzzy tolerance for red values.
      *
      * If non zero, the pixel's red component can deviate from values specified in this object by a maximum of this tolerance amount.
      *
      * \since QGIS 3.40
      */
#ifndef SIP_RUN
        double fuzzyToleranceRed = 4 * std::numeric_limits<double>::epsilon();
#else
        double fuzzyToleranceRed;
#endif

        /**
      * Fuzzy tolerance for green values.
      *
      * If non zero, the pixel's green component can deviate from values specified in this object by a maximum of this tolerance amount.
      *
      * \since QGIS 3.40
      */
#ifndef SIP_RUN
        double fuzzyToleranceGreen = 4 * std::numeric_limits<double>::epsilon();
#else
        double fuzzyToleranceGreen;
#endif

        /**
      * Fuzzy tolerance for blue values.
      *
      * If non zero, the pixel's blue component can deviate from values specified in this object by a maximum of this tolerance amount.
      *
      * \since QGIS 3.40
      */
#ifndef SIP_RUN
        double fuzzyToleranceBlue = 4 * std::numeric_limits<double>::epsilon();
#else
        double fuzzyToleranceBlue;
#endif

        bool operator==( const QgsRasterTransparency::TransparentThreeValuePixel &other ) const
        {
          return qgsDoubleNear( red, other.red )
                 && qgsDoubleNear( green, other.green )
                 && qgsDoubleNear( blue, other.blue )
                 && qgsDoubleNear( opacity, other.opacity )
                 && qgsDoubleNear( fuzzyToleranceRed, other.fuzzyToleranceRed )
                 && qgsDoubleNear( fuzzyToleranceGreen, other.fuzzyToleranceGreen )
                 && qgsDoubleNear( fuzzyToleranceBlue, other.fuzzyToleranceBlue );
        }
        bool operator!=( const QgsRasterTransparency::TransparentThreeValuePixel &other ) const { return !( *this == other ); }

#ifdef SIP_RUN
        // clang-format off
      SIP_PYOBJECT __repr__();
      % MethodCode
      QString str;
      if ( !qgsDoubleNear( sipCpp->fuzzyToleranceRed, 0 ) || !qgsDoubleNear( sipCpp->fuzzyToleranceGreen, 0 ) || !qgsDoubleNear( sipCpp->fuzzyToleranceBlue, 0 ) )
        str = u"<QgsRasterTransparency.TransparentThreeValuePixel: %1, %2, %3, %4, %5, %6, %7>"_s.arg( sipCpp->red ).arg( sipCpp->green ).arg( sipCpp->blue ).arg( sipCpp->opacity ).arg( sipCpp->fuzzyToleranceRed ).arg( sipCpp->fuzzyToleranceGreen ).arg( sipCpp->fuzzyToleranceBlue );
      else
        str = u"<QgsRasterTransparency.TransparentThreeValuePixel: %1, %2, %3, %4>"_s.arg( sipCpp->red ).arg( sipCpp->green ).arg( sipCpp->blue ).arg( sipCpp->opacity );
      sipRes = PyUnicode_FromString( str.toUtf8().constData() );
      % End
// clang-format on
#endif
    };

    /**
     * \ingroup core
     * \brief Defines the transparency for a range of single-band pixel values.
     */
    struct TransparentSingleValuePixel
    {
        /**
       * Constructor for TransparentSingleValuePixel.
       * \param minimum minimum pixel value to include in range
       * \param maximum maximum pixel value to include in range
       * \param opacity opacity for pixel, between 0 and 1.0
       * \param includeMinimum whether the minimum value should be included in the range
       * \param includeMaximum whether the maximum value should be included in the range
       *
       * \since QGIS 3.38
       */
        TransparentSingleValuePixel( double minimum = 0, double maximum = 0, double opacity = 0, bool includeMinimum = true, bool includeMaximum = true )
          : min( minimum )
          , max( maximum )
          , opacity( opacity )
          , includeMinimum( includeMinimum )
          , includeMaximum( includeMaximum )
        {}

        /**
       * Minimum pixel value to include in range.
       */
        double min;

        /**
       * Maximum pixel value to include in range.
       */
        double max;

        /**
       * Opacity for pixel, between 0 and 1.0.
       *
       * \since QGIS 3.38
       */
        double opacity = 0;

        /**
       * TRUE if pixels matching the min value should be considered transparent,
       * or FALSE if only pixels greater than the min value should be transparent.
       *
       * \since QGIS 3.38
       */
        bool includeMinimum = true;

        /**
       * TRUE if pixels matching the max value should be considered transparent,
       * or FALSE if only pixels less than the max value should be transparent.
       *
       * \since QGIS 3.38
       */
        bool includeMaximum = true;

        bool operator==( const QgsRasterTransparency::TransparentSingleValuePixel &other ) const
        {
          return qgsDoubleNear( min, other.min )
                 && qgsDoubleNear( max, other.max )
                 && qgsDoubleNear( opacity, other.opacity )
                 && includeMinimum == other.includeMinimum
                 && includeMaximum == other.includeMaximum;
        }
        bool operator!=( const QgsRasterTransparency::TransparentSingleValuePixel &other ) const { return !( *this == other ); }

#ifdef SIP_RUN
        // clang-format off
      SIP_PYOBJECT __repr__();
      % MethodCode
      const QString str = u"<QgsRasterTransparency.TransparentSingleValuePixel: %1, %2, %3>"_s.arg( sipCpp->min ).arg( sipCpp->max ).arg( sipCpp->opacity );
      sipRes = PyUnicode_FromString( str.toUtf8().constData() );
      % End
// clang-format on
#endif
    };

    /**
     * Returns the transparent single value pixel list.
     * \see setTransparentSingleValuePixelList()
     */
    QVector<QgsRasterTransparency::TransparentSingleValuePixel> transparentSingleValuePixelList() const;

    /**
     * Returns the transparent three value pixel list.
     * \see setTransparentThreeValuePixelList()
     */
    QVector<QgsRasterTransparency::TransparentThreeValuePixel> transparentThreeValuePixelList() const;

    /**
     * Resets the transparency list to a single \a value.
     */
    void initializeTransparentPixelList( double value );

    /**
     * Resets the transparency list to single red, green, and blue values.
     */
    void initializeTransparentPixelList( double redValue, double greenValue, double blueValue );

    /**
     * Sets the transparent single value pixel list, replacing the whole existing list.
     * \see transparentSingleValuePixelList()
     */
    void setTransparentSingleValuePixelList( const QVector<QgsRasterTransparency::TransparentSingleValuePixel> &newList );

    /**
     * Sets the transparent three value pixel list, replacing the whole existing list.
     * \see transparentThreeValuePixelList()
     */
    void setTransparentThreeValuePixelList( const QVector<QgsRasterTransparency::TransparentThreeValuePixel> &newList );

    /**
     * Returns the transparency value for a single \a value pixel.
     *
     * Searches through the transparency list, and if a match is found, the global transparency value is scaled
     * by the stored transparency value.
     *
     * \param value the needle to search for in the transparency hay stack
     * \param globalTransparency the overall transparency level for the layer
     *
     * \deprecated QGIS 3.40. Use opacityForValue() instead.
    */
    Q_DECL_DEPRECATED int alphaValue( double value, int globalTransparency = 255 ) const SIP_DEPRECATED;

    /**
     * Returns the opacity (as a value from 0 to 1) for a single \a value pixel.
     *
     * Searches through the transparency list, and if a match is found, returns
     * the opacity corresponding to the value. Returns 1 if no matches are found.
     *
     * \since QGIS 3.38
    */
    double opacityForValue( double value ) const;

    /**
     * Returns the transparency value for a RGB pixel.
     *
     * Searches through the transparency list, if a match is found, the global transparency value is scaled
     * by the stored transparency value.
     * \param redValue the red portion of the needle to search for in the transparency hay stack
     * \param greenValue  the green portion of the needle to search for in the transparency hay stack
     * \param blueValue the green portion of the needle to search for in the transparency hay stack
     * \param globalTransparency the overall transparency level for the layer
     *
     * \deprecated QGIS 3.40. Use opacityForRgbValues() instead.
    */
    Q_DECL_DEPRECATED int alphaValue( double redValue, double greenValue, double blueValue, int globalTransparency = 255 ) const SIP_DEPRECATED;

    /**
     * Returns the opacity (as a value from 0 to 1) for a set of RGB pixel values.
     *
     * Searches through the transparency list, and if a match is found, returns
     * the opacity corresponding to the values. Returns 1 if no matches are found.
     *
     * If any of the red, green or blue values are NaN, 0 will be returned.
     *
     * \since QGIS 3.38
    */
    double opacityForRgbValues( double redValue, double greenValue, double blueValue ) const;

    //! True if there are no entries in the pixel lists except the nodata value
    bool isEmpty() const;

    /**
     * Writes the transparency information to an XML document.
     */
    void writeXml( QDomDocument &doc, QDomElement &parentElem ) const;

    /**
     * Reads the transparency information from an XML document.
     */
    void readXml( const QDomElement &elem );

  private:
    //! \brief The list to hold transparency values for RGB layers
    QVector<QgsRasterTransparency::TransparentThreeValuePixel> mTransparentThreeValuePixelList;

    //! \brief The list to hold transparency values for single value pixel layers
    QVector<QgsRasterTransparency::TransparentSingleValuePixel> mTransparentSingleValuePixelList;
};
#endif
