/*
 * QRoundProgressBar - a circular progress bar Qt widget.
 *
 * Sintegrial Technologies (c) 2015-now
 *
 * The software is freeware and is distributed "as is" with the complete source codes.
 * Anybody is free to use it in any software projects, either commercial or non-commercial.
 * Please do not remove this copyright message and remain the name of the author unchanged.
 *
 * It is very appreciated if you produce some feedback to us case you are going to use
 * the software.
 *
 * Please send your questions, suggestions, and information about found issues to the
 *
 * sintegrial@gmail.com
 *
 */

#ifndef QROUNDPROGRESSBAR_H
#define QROUNDPROGRESSBAR_H

#include <QWidget>

/**
 * @brief The QRoundProgressBar class represents a circular progress bar and maintains its API
 * similar to the *QProgressBar*.
 *
 * ### Styles
 * QRoundProgressBar currently supports Donut, Pie and Line styles. See setBarStyle() for more details.
 *
 * ### Colors
 * Generally QRoundProgressBar uses its palette and font attributes to define how it will look.
 *
 * The following \a QPalette members are considered:
 * - *QPalette::Window*   background of the whole widget (normally should be set to Qt::NoBrush)
 * - *QPalette::Base*     background of the non-filled progress bar area (should be set to Qt::NoBrush to make it transparent)
 * - *QPalette::AlternateBase*  background of the central circle where the text is shown (for \a Donut style)
 * - *QPalette::Shadow*         foreground of the non-filled progress bar area (i.e. border color)
 * - *QPalette::Highlight*      background of the filled progress bar area
 * - *QPalette::Text*           color of the text shown in the center
 *
 * Create a \a QPalette with given attributes and apply it via `setPalette()`.
 *
 * ### Color gradient
 * \a Donut and \a Pie styles allow to use color gradient for currernt value area instead of plain brush fill.
 * See setDataColors() for more details.
 *
 * ### Value text
 * Value text is generally drawn inside the QRoundProgressBar using its `font()` and \a QPalette::Text role from its `palette()`.
 *
 * To define pattern of the text, use setFormat() function (see Qt's \a QProgressBar for more details).
 *
 * To define number of decimals to be shown, use setDecimals() function.
 *
 * ### Font
 * To use own font for value text, apply it via `setFont()`.
 *
 * By default, font size will be adjusted automatically to fit the inner circle of the widget.
 */
class QRoundProgressBar : public QWidget
{
    Q_OBJECT
  public:
    explicit QRoundProgressBar(QWidget *parent = nullptr);

    static const int PositionLeft   = 180;
    static const int PositionTop    = 90;
    static const int PositionRight  = 0;
    static const int PositionBottom = -90;

    /**
         * @brief Return position (in degrees) of minimum value.
         * \sa setNullPosition
         */
    double nullPosition() const { return m_nullPosition; }
    /**
         * @brief Defines position of minimum value.
         * @param position position on the circle (in degrees) of minimum value
         * \sa nullPosition
         */
    void setNullPosition(double position);

    /**
         * @brief The BarStyle enum defines general look of the progress bar.
         */
    enum BarStyle
    {
        /// Donut style (filled torus around the text)
        StyleDonut,
        /// Pie style (filled pie segment with the text in center)
        StylePie,
        /// Line style (thin round line around the text)
        StyleLine
    };
    /**
         * @brief Sets visual style of the widget.
         * \sa barStyle
         */
    void setBarStyle(BarStyle style);
    /**
         * @brief Returns current progree bar style.
         * \sa setBarStyle
         */
    BarStyle barStyle() const { return m_barStyle; }

    /**
         * @brief Sets width of the outline circle pen.
         * @param penWidth width of the outline circle pen (in pixels)
         */
    void setOutlinePenWidth(double penWidth);
    /**
         * @brief Returns width of the outline circle pen.
         */
    double outlinePenWidth() const { return m_outlinePenWidth; }

    /**
         * @brief Sets width of the data circle pen.
         * @param penWidth width of the data circle pen (in pixels)
         */
    void setDataPenWidth(double penWidth);
    /**
         * @brief Returns width of the data circle pen.
         */
    double dataPenWidth() const { return m_dataPenWidth; }

    /**
         * @brief Sets colors of the visible data and makes gradient brush from them.
         * Gradient colors can be set for \a Donut and \a Pie styles (see setBarStyle() function).
         *
         * *Warning*: this function will override widget's `palette()` to set dynamically created gradient brush.
         *
         * @param stopPoints List of colors (should have at least 2 values, see Qt's \a QGradientStops for more details).
         * Color value at point 0 corresponds to the minimum() value, while color value at point 1
         * corresponds to the maximum(). Other colors will be distributed accordingly to the defined ranges (see setRange()).
         */
    void setDataColors(const QGradientStops &stopPoints);

    /**
         * @brief Defines the string used to generate the current text.
         * If no format is set, no text will be shown.
         * @param format see \a QProgressBar's format description
         * \sa setDecimals
         */
    void setFormat(const QString &format);
    /**
         * @brief Sets format string to empty string. No text will be shown therefore.
         * See setFormat() for more information.
         */
    void resetFormat();
    /**
         * @brief Returns the string used to generate the current text.
         */
    QString format() const { return m_format; }

    /**
         * @brief Sets number of decimals to show after the comma (default is 1).
         * \sa setFormat
         */
    void setDecimals(int count);
    /**
         * @brief Returns number of decimals to show after the comma (default is 1).
         * \sa setFormat, setDecimals
         */
    int decimals() const { return m_decimals; }

    /**
         * @brief Returns current value shown on the widget.
         * \sa setValue()
         */
    double value() const { return m_value; }
    /**
         * @brief Returns minimum of the allowed value range.
         * \sa setMinimum, setRange
         */
    double minimum() const { return m_min; }
    /**
         * @brief Returns maximum of the allowed value range.
        * \sa setMaximum, setRange
         */
    double maximum() const { return m_max; }

  public Q_SLOTS:
    /**
         * @brief Defines minimum und maximum of the allowed value range.
         * If the current value does not fit into the range, it will be automatically adjusted.
         * @param min minimum of the allowed value range
         * @param max maximum of the allowed value range
         */
    void setRange(double min, double max);
    /**
         * @brief Defines minimum of the allowed value range.
         * If the current value does not fit into the range, it will be automatically adjusted.
         * @param min minimum of the allowed value range
         * \sa setRange
         */
    void setMinimum(double min);
    /**
         * @brief Defines maximum of the allowed value range.
         * If the current value does not fit into the range, it will be automatically adjusted.
         * @param max maximum of the allowed value range
         * \sa setRange
         */
    void setMaximum(double max);
    /**
         * @brief Sets a value which will be shown on the widget.
         * @param val must be between minimum() and maximum()
         */
    void setValue(double val);
    /**
         * @brief Integer version of the previous slot.
         * @param val must be between minimum() and maximum()
         */
    void setValue(int val);

  protected:
    void paintEvent(QPaintEvent *event) override;
    virtual void drawBackground(QPainter &p, const QRectF &baseRect);
    virtual void drawBase(QPainter &p, const QRectF &baseRect);
    virtual void drawValue(QPainter &p, const QRectF &baseRect, double value, double arcLength);
    virtual void calculateInnerRect(const QRectF &baseRect, double outerRadius, QRectF &innerRect, double &innerRadius);
    virtual void drawInnerBackground(QPainter &p, const QRectF &innerRect);
    virtual void drawText(QPainter &p, const QRectF &innerRect, double innerRadius, double value);
    virtual QString valueToText(double value) const;
    virtual void valueFormatChanged();

    QSize minimumSizeHint() const override { return QSize(32, 32); }

    bool hasHeightForWidth() const override { return true; }
    int heightForWidth(int w) const override { return w; }

    void rebuildDataBrushIfNeeded();

    double m_min, m_max;
    double m_value;

    double m_nullPosition;
    BarStyle m_barStyle;
    double m_outlinePenWidth, m_dataPenWidth;

    QGradientStops m_gradientData;
    bool m_rebuildBrush;

    QString m_format;
    int m_decimals;

    static const int UF_VALUE   = 1;
    static const int UF_PERCENT = 2;
    static const int UF_MAX     = 4;
    int m_updateFlags;
};

#endif // QROUNDPROGRESSBAR_H
