// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#undef QT_NO_FOREACH // this file contains unported legacy Q_FOREACH uses

#include <QtCharts/QPieSeries>
#include <private/qpieseries_p.h>
#include <QtCharts/QPieSlice>
#include <private/qpieslice_p.h>
#include <private/pieslicedata_p.h>
#include <private/chartdataset_p.h>
#include <private/charttheme_p.h>
#include <QtCharts/QAbstractAxis>
#include <private/pieanimation_p.h>
#include <private/charthelpers_p.h>

#include <QtCharts/QPieLegendMarker>

QT_BEGIN_NAMESPACE

/*!
    \class QPieSeries
    \inmodule QtCharts
    \brief The QPieSeries class presents data in pie charts.

    A pie series consists of slices that are defined as QPieSlice objects.
    The slices can have any values as the QPieSeries object calculates
    the percentage of a slice compared with the sum of all slices in the series
    to determine the actual size of the slice in the chart.

    Pie size and position on the chart are controlled by using relative values
    that range from 0.0 to 1.0.
    These relate to the actual chart rectangle.

    By default, the pie is defined as a full pie. A partial pie can be created
    by setting a starting angle and angle span for the series.
    A full pie is 360 degrees, where 0 is at 12 a'clock.

    See the \l {Charts with Widgets Gallery} to learn how to use QPieSeries.
    \image examples_piechart.png
    \image examples_donutchart.png

    \sa QPieSlice, QChart
*/
/*!
    \qmltype PieSeries
    \instantiates QPieSeries
    \inqmlmodule QtCharts

    \inherits AbstractSeries

    \brief Presents data in pie charts.

    A pie series consists of slices that are defined using the PieSlice type.
    The slices can have any values as the PieSeries type calculates
    the percentage of a slice compared with the sum of all slices in the series
    to determine the actual size of the slice in the chart.

    Pie size and position on the chart are controlled by using relative values
    that range from 0.0 to 1.0.
    These relate to the actual chart rectangle.

    By default, the pie is defined as a full pie. A partial pie can be created
    by setting a starting angle and angle span for the series.
    A full pie is 360 degrees, where 0 is at 12 a'clock.

    The following QML example shows how to create a simple pie chart.

    \snippet qmlchartsgallery/qml/PieChart.qml 1

    \beginfloatleft
    \image examples_qmlchart1.png
    \endfloat
    \clearfloat

    \sa PieSlice, ChartView
*/

/*!
    \property QPieSeries::horizontalPosition
    \brief The horizontal position of the pie.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the absolute left.
    \li 1.0 is the absolute right.
    \endlist
    The default value is 0.5 (center).
    \sa verticalPosition
*/

/*!
    \qmlproperty real PieSeries::horizontalPosition

    The horizontal position of the pie.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the absolute left.
    \li 1.0 is the absolute right.
    \endlist
    The default value is 0.5 (center).
    \sa verticalPosition
*/

/*!
    \property QPieSeries::verticalPosition
    \brief The vertical position of the pie.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the absolute top.
    \li 1.0 is the absolute bottom.
    \endlist
    The default value is 0.5 (center).
    \sa horizontalPosition
*/

/*!
    \qmlproperty real PieSeries::verticalPosition

    The vertical position of the pie.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the absolute top.
    \li 1.0 is the absolute bottom.
    \endlist
    The default value is 0.5 (center).
    \sa horizontalPosition
*/

/*!
    \property QPieSeries::size
    \brief The pie size.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the minimum size (pie not drawn).
    \li 1.0 is the maximum size that can fit the chart.
    \endlist

    When setting this property, the holeSize property is adjusted if necessary,
    to ensure that the hole size is not greater than the pie size.

    The default value is 0.7.
*/

/*!
    \qmlproperty real PieSeries::size

    The pie size.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the minimum size (pie not drawn).
    \li 1.0 is the maximum size that can fit the chart.
    \endlist

    When setting this property, the holeSize property is adjusted if necessary,
    to ensure that the hole size is not greater than the pie size.

    The default value is 0.7.
*/

/*!
    \property QPieSeries::holeSize
    \brief The donut hole size.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the minimum size (full pie drawn without a hole).
    \li 1.0 is the maximum size that can fit the chart (the donut has no width).
    \endlist

    When setting this property, the \l size property is adjusted if necessary,
    to ensure that the hole size is not greater than the pie size.

    The default value is 0.0.
*/

/*!
    \qmlproperty real PieSeries::holeSize

    The donut hole size.

    The value is relative to the chart rectangle, so that:

    \list
    \li 0.0 is the minimum size (full pie drawn without a hole).
    \li 1.0 is the maximum size that can fit the chart (the donut has no width).
    \endlist

    When setting this property, the \l size property is adjusted if necessary,
    to ensure that the hole size is not greater than the pie size.

    The default value is 0.0.
*/

/*!
    \property QPieSeries::startAngle
    \brief The starting angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    The default value is 0.
*/

/*!
    \qmlproperty real PieSeries::startAngle

    The starting angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    The default value is 0.
*/

/*!
    \property QPieSeries::endAngle
    \brief The ending angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    The default value is 360.
*/

/*!
    \qmlproperty real PieSeries::endAngle

    The ending angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    The default value is 360.
*/

/*!
    \property QPieSeries::count

    \brief The number of slices in the series.
*/

/*!
    \qmlproperty int PieSeries::count

    The number of slices in the series.
*/

/*!
    \fn void QPieSeries::countChanged()
    This signal is emitted when the slice count changes.
    \sa count
*/

/*!
    \property QPieSeries::sum

    \brief The sum of all slices.

    The series keeps track of the sum of all the slices it holds.
*/

/*!
    \qmlproperty real PieSeries::sum

    The sum of all slices.

    The series keeps track of the sum of all the slices it holds.
*/

/*!
    \fn void QPieSeries::sumChanged()
    This signal is emitted when the sum of all slices changes.
    \sa sum
*/

/*!
    \fn void QPieSeries::added(const QList<QPieSlice*> &slices)

    This signal is emitted when the slices specified by \a slices are added to the series.

    \sa append(), insert()
*/
/*!
    \qmlsignal PieSeries::added(list<PieSlice> slices)
    This signal is emitted when the slices specified by \a slices are added to the series.

    The corresponding signal handler is \c onAdded.
*/

/*!
    \fn void QPieSeries::removed(const QList<QPieSlice*> &slices)
    This signal is emitted when the slices specified by \a slices are removed from the series.
    \sa remove()
*/
/*!
    \qmlsignal PieSeries::removed(list<PieSlice> slices)
    This signal is emitted when the slices specified by \a slices are removed from the series.

    The corresponding signal handler is \c onRemoved.
*/

/*!
    \qmlsignal PieSeries::sliceAdded(PieSlice slice)
    This signal is emitted when the slice specified by \a slice is added to the series.

    The corresponding signal handler is \c onSliceAdded.
*/

/*!
    \qmlsignal PieSeries::sliceRemoved(PieSlice slice)
    This signal is emitted when the slice specified by \a slice is removed from the series.

    The corresponding signal handler is \c onSliceRemoved.
*/

/*!
    \fn void QPieSeries::clicked(QPieSlice *slice)
    This signal is emitted when the slice specified by \a slice is clicked.
    \sa QPieSlice::clicked()
*/
/*!
  \qmlsignal PieSeries::clicked(PieSlice slice)
  This signal is emitted when the slice specified by \a slice is clicked.

  The corresponding signal handler is \c onClicked.
*/

/*!
    \fn void QPieSeries::pressed(QPieSlice *slice)
    This signal is emitted when the user clicks the slice specified by \a slice
    and holds down the mouse button.
    \sa QPieSlice::pressed()
*/
/*!
    \qmlsignal PieSeries::pressed(PieSlice slice)
    This signal is emitted when the user clicks the slice specified by \a slice
    and holds down the mouse button.

    The corresponding signal handler is \c onPressed.
*/

/*!
    \fn void QPieSeries::released(QPieSlice *slice)
    This signal is emitted when the user releases the mouse press on the slice
    specified by \a slice.
    \sa QPieSlice::released()
*/
/*!
    \qmlsignal PieSeries::released(PieSlice slice)
    This signal is emitted when the user releases the mouse press on the slice
    specified by \a slice.

    The corresponding signal handler is \c onReleased.
*/

/*!
    \fn void QPieSeries::doubleClicked(QPieSlice *slice)
    This signal is emitted when the slice specified by \a slice is double-clicked.
    \sa QPieSlice::doubleClicked()
*/
/*!
    \qmlsignal PieSeries::doubleClicked(PieSlice slice)
    This signal is emitted when the slice specified by \a slice is double-clicked.

    The corresponding signal handler is \c onDoubleClicked.
*/

/*!
    \fn void QPieSeries::hovered(QPieSlice* slice, bool state)
    This signal is emitted when a mouse is hovered over the slice specified by
    \a slice. When the mouse moves over the slice, \a state turns \c true, and
    when the mouse moves away again, it turns \c false.
    \sa QPieSlice::hovered()
*/
/*!
    \qmlsignal PieSeries::hovered(PieSlice slice, bool state)
    This signal is emitted when a mouse is hovered over the slice specified by
    \a slice. When the mouse moves over the slice, \a state turns \c true, and
    when the mouse moves away again, it turns \c false.

    The corresponding signal handler is \c onHovered.
*/

/*!
    \qmlmethod PieSlice PieSeries::at(int index)
    Returns the slice at the position specified by \a index. Returns null if the
    index is not valid.
*/

/*!
    \qmlmethod PieSlice PieSeries::find(string label)
    Returns the first slice that has the label \a label. Returns null if the label
    is not found.
*/

/*!
    \qmlmethod PieSlice PieSeries::append(string label, real value)
    Adds a new slice with the label \a label and the value \a value to the pie.
*/

/*!
    \qmlmethod bool PieSeries::remove(PieSlice slice)
    Removes the slice specified by \a slice from the pie. Returns \c true if the
    removal was successful, \c false otherwise.
*/

/*!
    \qmlmethod PieSeries::clear()
    Removes all slices from the pie.
*/

/*!
    Constructs a series object that is a child of \a parent.
*/
QPieSeries::QPieSeries(QObject *parent)
    : QAbstractSeries(*new QPieSeriesPrivate(this), parent)
{
    Q_D(QPieSeries);
    QObject::connect(this, SIGNAL(countChanged()), d, SIGNAL(countChanged()));
}

/*!
    Removes the pie series and its slices.
*/
QPieSeries::~QPieSeries()
{
    // NOTE: d_prt destroyed by QObject
    clear();
}

/*!
    \reimp

    Returns the type of the series.
*/
QAbstractSeries::SeriesType QPieSeries::type() const
{
    return QAbstractSeries::SeriesTypePie;
}

/*!
    Appends the slice specified by \a slice to the series.
    Slice ownership is passed to the series.

    Returns \c true if appending succeeds.
*/
bool QPieSeries::append(QPieSlice *slice)
{
    return append(QList<QPieSlice *>() << slice);
}

/*!
    Appends the array of slices specified by \a slices to the series.
    Slice ownership is passed to the series.

    Returns \c true if appending succeeds.
*/
bool QPieSeries::append(const QList<QPieSlice *> &slices)
{
    Q_D(QPieSeries);

    if (slices.size() == 0)
        return false;

    for (auto *s : slices) {
        if (!s || d->m_slices.contains(s))
            return false;
        if (s->series()) // already added to some series
            return false;
        if (!isValidValue(s->value()))
            return false;
    }

    for (auto *s : slices) {
        s->setParent(this);
        QPieSlicePrivate::fromSlice(s)->m_series = this;
        d->m_slices << s;
    }

    d->updateDerivativeData();

    for (auto *s : slices) {
        connect(s, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
        connect(s, SIGNAL(clicked()), d, SLOT(sliceClicked()));
        connect(s, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
        connect(s, SIGNAL(pressed()), d, SLOT(slicePressed()));
        connect(s, SIGNAL(released()), d, SLOT(sliceReleased()));
        connect(s, SIGNAL(doubleClicked()), d, SLOT(sliceDoubleClicked()));
    }

    emit added(slices);
    emit countChanged();

    return true;
}

/*!
    Appends the slice specified by \a slice to the series and returns a reference to the series.
    Slice ownership is passed to the series.
*/
QPieSeries &QPieSeries::operator << (QPieSlice *slice)
{
    append(slice);
    return *this;
}


/*!
    Appends a single slice with the specified \a value and \a label to the series.
    Slice ownership is passed to the series.
    Returns null if \a value is \c NaN, \c Inf, or \c -Inf and adds nothing to the
    series.
*/
QPieSlice *QPieSeries::append(const QString &label, qreal value)
{
    if (isValidValue(value)) {
        QPieSlice *slice = new QPieSlice(label, value);
        append(slice);
        return slice;
    } else {
        return 0;
    }
}

/*!
    Inserts the slice specified by \a slice to the series before the slice at
    the position specified by \a index.
    Slice ownership is passed to the series.

    Returns \c true if inserting succeeds.
*/
bool QPieSeries::insert(int index, QPieSlice *slice)
{
    Q_D(QPieSeries);

    if (index < 0 || index > d->m_slices.size())
        return false;

    if (!slice || d->m_slices.contains(slice))
        return false;

    if (slice->series()) // already added to some series
        return false;

    if (!isValidValue(slice->value()))
        return false;

    slice->setParent(this);
    QPieSlicePrivate::fromSlice(slice)->m_series = this;
    d->m_slices.insert(index, slice);

    d->updateDerivativeData();

    connect(slice, SIGNAL(valueChanged()), d, SLOT(sliceValueChanged()));
    connect(slice, SIGNAL(clicked()), d, SLOT(sliceClicked()));
    connect(slice, SIGNAL(hovered(bool)), d, SLOT(sliceHovered(bool)));
    connect(slice, SIGNAL(pressed()), d, SLOT(slicePressed()));
    connect(slice, SIGNAL(released()), d, SLOT(sliceReleased()));
    connect(slice, SIGNAL(doubleClicked()), d, SLOT(sliceDoubleClicked()));

    emit added(QList<QPieSlice *>() << slice);
    emit countChanged();

    return true;
}

/*!
    Removes a single slice, specified by \a slice, from the series and deletes it
    permanently.

    The pointer cannot be referenced after this call.

    Returns \c true if the removal succeeds.
*/
bool QPieSeries::remove(QPieSlice *slice)
{
    Q_D(QPieSeries);

    if (!d->m_slices.removeOne(slice))
        return false;

    d->updateDerivativeData();

    emit removed(QList<QPieSlice *>() << slice);
    emit countChanged();

    delete slice;
    slice = 0;

    return true;
}

/*!
    Takes a single slice, specified by \a slice, from the series. Does not delete
    the slice object.

    \note The series remains the slice's parent object. You must set the
    parent object to take full ownership.

    Returns \c true if the take operation was successful.
*/
bool QPieSeries::take(QPieSlice *slice)
{
    Q_D(QPieSeries);

    if (!d->m_slices.removeOne(slice))
        return false;

    QPieSlicePrivate::fromSlice(slice)->m_series = 0;
    slice->disconnect(d);

    d->updateDerivativeData();

    emit removed(QList<QPieSlice *>() << slice);
    emit countChanged();

    return true;
}

/*!
    Clears all slices from the series.
*/
void QPieSeries::clear()
{
    Q_D(QPieSeries);
    if (d->m_slices.size() == 0)
        return;

    QList<QPieSlice *> slices = d->m_slices;
    foreach (QPieSlice *s, d->m_slices)
        d->m_slices.removeOne(s);

    d->updateDerivativeData();

    emit removed(slices);
    emit countChanged();

    foreach (QPieSlice *s, slices)
        delete s;
}

/*!
    Returns a list of slices that belong to this series.
*/
QList<QPieSlice *> QPieSeries::slices() const
{
    Q_D(const QPieSeries);
    return d->m_slices;
}

/*!
    Returns the number of the slices in this series.
*/
int QPieSeries::count() const
{
    Q_D(const QPieSeries);
    return d->m_slices.size();
}

/*!
    Returns \c true if the series is empty.
*/
bool QPieSeries::isEmpty() const
{
    Q_D(const QPieSeries);
    return d->m_slices.isEmpty();
}

/*!
    Returns the sum of all slice values in this series.

    \sa QPieSlice::value(), QPieSlice::setValue(), QPieSlice::percentage()
*/
qreal QPieSeries::sum() const
{
    Q_D(const QPieSeries);
    return d->m_sum;
}

void QPieSeries::setHoleSize(qreal holeSize)
{
    Q_D(QPieSeries);
    holeSize = qBound((qreal)0.0, holeSize, (qreal)1.0);
    d->setSizes(holeSize, qMax(d->m_pieRelativeSize, holeSize));
}

qreal QPieSeries::holeSize() const
{
    Q_D(const QPieSeries);
    return d->m_holeRelativeSize;
}

void QPieSeries::setHorizontalPosition(qreal relativePosition)
{
    Q_D(QPieSeries);

    if (relativePosition < 0.0)
        relativePosition = 0.0;
    if (relativePosition > 1.0)
        relativePosition = 1.0;

    if (!qFuzzyCompare(d->m_pieRelativeHorPos, relativePosition)) {
        d->m_pieRelativeHorPos = relativePosition;
        emit d->horizontalPositionChanged();
    }
}

qreal QPieSeries::horizontalPosition() const
{
    Q_D(const QPieSeries);
    return d->m_pieRelativeHorPos;
}

void QPieSeries::setVerticalPosition(qreal relativePosition)
{
    Q_D(QPieSeries);

    if (relativePosition < 0.0)
        relativePosition = 0.0;
    if (relativePosition > 1.0)
        relativePosition = 1.0;

    if (!qFuzzyCompare(d->m_pieRelativeVerPos, relativePosition)) {
        d->m_pieRelativeVerPos = relativePosition;
        emit d->verticalPositionChanged();
    }
}

qreal QPieSeries::verticalPosition() const
{
    Q_D(const QPieSeries);
    return d->m_pieRelativeVerPos;
}

void QPieSeries::setPieSize(qreal relativeSize)
{
    Q_D(QPieSeries);
    relativeSize = qBound((qreal)0.0, relativeSize, (qreal)1.0);
    d->setSizes(qMin(d->m_holeRelativeSize, relativeSize), relativeSize);

}

qreal QPieSeries::pieSize() const
{
    Q_D(const QPieSeries);
    return d->m_pieRelativeSize;
}


void QPieSeries::setPieStartAngle(qreal angle)
{
    Q_D(QPieSeries);
    if (qFuzzyCompare(d->m_pieStartAngle, angle))
        return;
    d->m_pieStartAngle = angle;
    d->updateDerivativeData();
    emit d->pieStartAngleChanged();
}

qreal QPieSeries::pieStartAngle() const
{
    Q_D(const QPieSeries);
    return d->m_pieStartAngle;
}

/*!
    Sets the end angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    \a angle must be greater than the start angle.

    \sa pieEndAngle(), pieStartAngle(), setPieStartAngle()
*/
void QPieSeries::setPieEndAngle(qreal angle)
{
    Q_D(QPieSeries);
    if (qFuzzyCompare(d->m_pieEndAngle, angle))
        return;
    d->m_pieEndAngle = angle;
    d->updateDerivativeData();
    emit d->pieEndAngleChanged();
}

/*!
    Returns the end angle of the pie.

    A full pie is 360 degrees, where 0 degrees is at 12 a'clock.

    \sa setPieEndAngle(), pieStartAngle(), setPieStartAngle()
*/
qreal QPieSeries::pieEndAngle() const
{
    Q_D(const QPieSeries);
    return d->m_pieEndAngle;
}

/*!
    Sets the visibility of all slice labels to \a visible.

    \note This function affects only the current slices in the series.
    If a new slice is added, the default label visibility is \c false.

    \sa QPieSlice::isLabelVisible(), QPieSlice::setLabelVisible()
*/
void QPieSeries::setLabelsVisible(bool visible)
{
    Q_D(QPieSeries);
    foreach (QPieSlice *s, d->m_slices)
        s->setLabelVisible(visible);
}

/*!
    Sets the position of all the slice labels to \a position.

    \note This function affects only the current slices in the series.
    If a new slice is added, the default label position is QPieSlice::LabelOutside.

    \sa QPieSlice::labelPosition(), QPieSlice::setLabelPosition()
*/
void QPieSeries::setLabelsPosition(QPieSlice::LabelPosition position)
{
    Q_D(QPieSeries);
    foreach (QPieSlice *s, d->m_slices)
        s->setLabelPosition(position);
}

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


QPieSeriesPrivate::QPieSeriesPrivate(QPieSeries *parent) :
    QAbstractSeriesPrivate(parent),
    m_pieRelativeHorPos(0.5),
    m_pieRelativeVerPos(0.5),
    m_pieRelativeSize(0.7),
    m_pieStartAngle(0),
    m_pieEndAngle(360),
    m_sum(0),
    m_holeRelativeSize(0.0)
{
}

QPieSeriesPrivate::~QPieSeriesPrivate()
{
}

void QPieSeriesPrivate::updateDerivativeData()
{
    // calculate sum of all slices
    qreal sum = 0;
    foreach (QPieSlice *s, m_slices)
        sum += s->value();

    if (!qFuzzyCompare(m_sum, sum)) {
        m_sum = sum;
        emit q_func()->sumChanged();
    }

    // nothing to show..
    if (qFuzzyCompare(m_sum, 0))
        return;

    // update slice attributes
    qreal sliceAngle = m_pieStartAngle;
    qreal pieSpan = m_pieEndAngle - m_pieStartAngle;
    QList<QPieSlice *> changed;
    foreach (QPieSlice *s, m_slices) {
        QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(s);
        d->setPercentage(s->value() / m_sum);
        d->setStartAngle(sliceAngle);
        d->setAngleSpan(pieSpan * s->percentage());
        sliceAngle += s->angleSpan();
    }


    emit calculatedDataChanged();
}

void QPieSeriesPrivate::setSizes(qreal innerSize, qreal outerSize)
{
    bool changed = false;

    if (!qFuzzyCompare(m_holeRelativeSize, innerSize)) {
        m_holeRelativeSize = innerSize;
        changed = true;
    }

    if (!qFuzzyCompare(m_pieRelativeSize, outerSize)) {
        m_pieRelativeSize = outerSize;
        changed = true;
    }

    if (changed)
        emit pieSizeChanged();
}

QPieSeriesPrivate *QPieSeriesPrivate::fromSeries(QPieSeries *series)
{
    return series->d_func();
}

void QPieSeriesPrivate::sliceValueChanged()
{
    Q_ASSERT(m_slices.contains(qobject_cast<QPieSlice *>(sender())));
    updateDerivativeData();
}

void QPieSeriesPrivate::sliceClicked()
{
    QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
    Q_ASSERT(m_slices.contains(slice));
    Q_Q(QPieSeries);
    emit q->clicked(slice);
}

void QPieSeriesPrivate::sliceHovered(bool state)
{
    QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
    if (!m_slices.isEmpty()) {
        Q_ASSERT(m_slices.contains(slice));
        Q_Q(QPieSeries);
        emit q->hovered(slice, state);
    }
}

void QPieSeriesPrivate::slicePressed()
{
    QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
    Q_ASSERT(m_slices.contains(slice));
    Q_Q(QPieSeries);
    emit q->pressed(slice);
}

void QPieSeriesPrivate::sliceReleased()
{
    QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
    Q_ASSERT(m_slices.contains(slice));
    Q_Q(QPieSeries);
    emit q->released(slice);
}

void QPieSeriesPrivate::sliceDoubleClicked()
{
    QPieSlice *slice = qobject_cast<QPieSlice *>(sender());
    Q_ASSERT(m_slices.contains(slice));
    Q_Q(QPieSeries);
    emit q->doubleClicked(slice);
}

void QPieSeriesPrivate::initializeDomain()
{
    // does not apply to pie
}

void QPieSeriesPrivate::initializeGraphics(QGraphicsItem* parent)
{
    Q_Q(QPieSeries);
    PieChartItem *pie = new PieChartItem(q,parent);
    m_item.reset(pie);
    QAbstractSeriesPrivate::initializeGraphics(parent);
}

void QPieSeriesPrivate::initializeAnimations(QChart::AnimationOptions options,
                                             int duration, QEasingCurve &curve)
{
    PieChartItem *item = static_cast<PieChartItem *>(m_item.get());
    Q_ASSERT(item);
    if (item->animation())
        item->animation()->stopAndDestroyLater();

    if (options.testFlag(QChart::SeriesAnimations))
        item->setAnimation(new PieAnimation(item, duration, curve));
    else
        item->setAnimation(0);
    QAbstractSeriesPrivate::initializeAnimations(options, duration, curve);
}

QList<QLegendMarker*> QPieSeriesPrivate::createLegendMarkers(QLegend* legend)
{
    Q_Q(QPieSeries);
    QList<QLegendMarker*> markers;
    const auto slices = q->slices();
    for (QPieSlice *slice : slices) {
        QPieLegendMarker* marker = new QPieLegendMarker(q,slice,legend);
        markers << marker;
    }
    return markers;
}

void QPieSeriesPrivate::initializeAxes()
{

}

QAbstractAxis::AxisType QPieSeriesPrivate::defaultAxisType(Qt::Orientation orientation) const
{
    Q_UNUSED(orientation);
    return QAbstractAxis::AxisTypeNoAxis;
}

QAbstractAxis* QPieSeriesPrivate::createDefaultAxis(Qt::Orientation orientation) const
{
    Q_UNUSED(orientation);
    return 0;
}

void QPieSeriesPrivate::initializeTheme(int index, ChartTheme* theme, bool forced)
{
    //Q_Q(QPieSeries);
    //const QList<QColor>& colors = theme->seriesColors();
    const QList<QGradient>& gradients = theme->seriesGradients();

    for (int i(0); i < m_slices.size(); i++) {

        QColor penColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), 0.0);

        // Get color for a slice from a gradient linearly, beginning from the start of the gradient
        qreal pos = (qreal)(i + 1) / (qreal) m_slices.size();
        QColor brushColor = ChartThemeManager::colorAt(gradients.at(index % gradients.size()), pos);

        QPieSlice *s = m_slices.at(i);
        QPieSlicePrivate *d = QPieSlicePrivate::fromSlice(s);

        if (forced || d->m_data.m_slicePen.isThemed())
            d->setPen(penColor, true);

        if (forced || d->m_data.m_sliceBrush.isThemed())
            d->setBrush(brushColor, true);

        if (forced || d->m_data.m_labelBrush.isThemed())
            d->setLabelBrush(theme->labelBrush().color(), true);

        if (forced || d->m_data.m_labelFont.isThemed())
            d->setLabelFont(theme->labelFont(), true);
    }
}

QT_END_NAMESPACE

#include "moc_qpieseries.cpp"
#include "moc_qpieseries_p.cpp"
