/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtQuick module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#ifndef QQUICKPATH_H
#define QQUICKPATH_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <private/qtquickglobal_p.h>

QT_REQUIRE_CONFIG(quick_path);

#include <qqml.h>

#include <private/qqmlnullablevalue_p.h>
#include <private/qbezier_p.h>
#include <private/qtquickglobal_p.h>

#include <QtCore/QObject>
#include <QtGui/QPainterPath>

QT_BEGIN_NAMESPACE

class QQuickCurve;
struct QQuickPathData
{
    int index;
    QPointF endPoint;
    QList<QQuickCurve*> curves;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathElement : public QObject
{
    Q_OBJECT
public:
    QQuickPathElement(QObject *parent=nullptr) : QObject(parent) {}
Q_SIGNALS:
    void changed();
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathAttribute : public QQuickPathElement
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
    Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
public:
    QQuickPathAttribute(QObject *parent=nullptr) : QQuickPathElement(parent) {}


    QString name() const;
    void setName(const QString &name);

    qreal value() const;
    void setValue(qreal value);

Q_SIGNALS:
    void nameChanged();
    void valueChanged();

private:
    QString _name;
    qreal _value = 0;
};

class Q_QUICK_PRIVATE_EXPORT QQuickCurve : public QQuickPathElement
{
    Q_OBJECT

    Q_PROPERTY(qreal x READ x WRITE setX NOTIFY xChanged)
    Q_PROPERTY(qreal y READ y WRITE setY NOTIFY yChanged)
    Q_PROPERTY(qreal relativeX READ relativeX WRITE setRelativeX NOTIFY relativeXChanged)
    Q_PROPERTY(qreal relativeY READ relativeY WRITE setRelativeY NOTIFY relativeYChanged)
public:
    QQuickCurve(QObject *parent=nullptr) : QQuickPathElement(parent) {}

    qreal x() const;
    void setX(qreal x);
    bool hasX();

    qreal y() const;
    void setY(qreal y);
    bool hasY();

    qreal relativeX() const;
    void setRelativeX(qreal x);
    bool hasRelativeX();

    qreal relativeY() const;
    void setRelativeY(qreal y);
    bool hasRelativeY();

    virtual void addToPath(QPainterPath &, const QQuickPathData &) {}

Q_SIGNALS:
    void xChanged();
    void yChanged();
    void relativeXChanged();
    void relativeYChanged();

private:
    QQmlNullableValue<qreal> _x;
    QQmlNullableValue<qreal> _y;
    QQmlNullableValue<qreal> _relativeX;
    QQmlNullableValue<qreal> _relativeY;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathLine : public QQuickCurve
{
    Q_OBJECT
public:
    QQuickPathLine(QObject *parent=nullptr) : QQuickCurve(parent) {}

    void addToPath(QPainterPath &path, const QQuickPathData &) override;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathMove : public QQuickCurve
{
    Q_OBJECT
public:
    QQuickPathMove(QObject *parent=nullptr) : QQuickCurve(parent) {}

    void addToPath(QPainterPath &path, const QQuickPathData &) override;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathQuad : public QQuickCurve
{
    Q_OBJECT

    Q_PROPERTY(qreal controlX READ controlX WRITE setControlX NOTIFY controlXChanged)
    Q_PROPERTY(qreal controlY READ controlY WRITE setControlY NOTIFY controlYChanged)
    Q_PROPERTY(qreal relativeControlX READ relativeControlX WRITE setRelativeControlX NOTIFY relativeControlXChanged)
    Q_PROPERTY(qreal relativeControlY READ relativeControlY WRITE setRelativeControlY NOTIFY relativeControlYChanged)
public:
    QQuickPathQuad(QObject *parent=nullptr) : QQuickCurve(parent) {}

    qreal controlX() const;
    void setControlX(qreal x);

    qreal controlY() const;
    void setControlY(qreal y);

    qreal relativeControlX() const;
    void setRelativeControlX(qreal x);
    bool hasRelativeControlX();

    qreal relativeControlY() const;
    void setRelativeControlY(qreal y);
    bool hasRelativeControlY();

    void addToPath(QPainterPath &path, const QQuickPathData &) override;

Q_SIGNALS:
    void controlXChanged();
    void controlYChanged();
    void relativeControlXChanged();
    void relativeControlYChanged();

private:
    qreal _controlX = 0;
    qreal _controlY = 0;
    QQmlNullableValue<qreal> _relativeControlX;
    QQmlNullableValue<qreal> _relativeControlY;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathCubic : public QQuickCurve
{
    Q_OBJECT

    Q_PROPERTY(qreal control1X READ control1X WRITE setControl1X NOTIFY control1XChanged)
    Q_PROPERTY(qreal control1Y READ control1Y WRITE setControl1Y NOTIFY control1YChanged)
    Q_PROPERTY(qreal control2X READ control2X WRITE setControl2X NOTIFY control2XChanged)
    Q_PROPERTY(qreal control2Y READ control2Y WRITE setControl2Y NOTIFY control2YChanged)
    Q_PROPERTY(qreal relativeControl1X READ relativeControl1X WRITE setRelativeControl1X NOTIFY relativeControl1XChanged)
    Q_PROPERTY(qreal relativeControl1Y READ relativeControl1Y WRITE setRelativeControl1Y NOTIFY relativeControl1YChanged)
    Q_PROPERTY(qreal relativeControl2X READ relativeControl2X WRITE setRelativeControl2X NOTIFY relativeControl2XChanged)
    Q_PROPERTY(qreal relativeControl2Y READ relativeControl2Y WRITE setRelativeControl2Y NOTIFY relativeControl2YChanged)
public:
    QQuickPathCubic(QObject *parent=nullptr) : QQuickCurve(parent) {}

    qreal control1X() const;
    void setControl1X(qreal x);

    qreal control1Y() const;
    void setControl1Y(qreal y);

    qreal control2X() const;
    void setControl2X(qreal x);

    qreal control2Y() const;
    void setControl2Y(qreal y);

    qreal relativeControl1X() const;
    void setRelativeControl1X(qreal x);
    bool hasRelativeControl1X();

    qreal relativeControl1Y() const;
    void setRelativeControl1Y(qreal y);
    bool hasRelativeControl1Y();

    qreal relativeControl2X() const;
    void setRelativeControl2X(qreal x);
    bool hasRelativeControl2X();

    qreal relativeControl2Y() const;
    void setRelativeControl2Y(qreal y);
    bool hasRelativeControl2Y();

    void addToPath(QPainterPath &path, const QQuickPathData &) override;

Q_SIGNALS:
    void control1XChanged();
    void control1YChanged();
    void control2XChanged();
    void control2YChanged();
    void relativeControl1XChanged();
    void relativeControl1YChanged();
    void relativeControl2XChanged();
    void relativeControl2YChanged();

private:
    qreal _control1X = 0;
    qreal _control1Y = 0;
    qreal _control2X = 0;
    qreal _control2Y = 0;
    QQmlNullableValue<qreal> _relativeControl1X;
    QQmlNullableValue<qreal> _relativeControl1Y;
    QQmlNullableValue<qreal> _relativeControl2X;
    QQmlNullableValue<qreal> _relativeControl2Y;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathCatmullRomCurve : public QQuickCurve
{
    Q_OBJECT
public:
    QQuickPathCatmullRomCurve(QObject *parent=nullptr) : QQuickCurve(parent) {}

    void addToPath(QPainterPath &path, const QQuickPathData &) override;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathArc : public QQuickCurve
{
    Q_OBJECT
    Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged)
    Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
    Q_PROPERTY(bool useLargeArc READ useLargeArc WRITE setUseLargeArc NOTIFY useLargeArcChanged)
    Q_PROPERTY(ArcDirection direction READ direction WRITE setDirection NOTIFY directionChanged)
    Q_PROPERTY(qreal xAxisRotation READ xAxisRotation WRITE setXAxisRotation NOTIFY xAxisRotationChanged REVISION 2)

public:
    QQuickPathArc(QObject *parent=nullptr)
        : QQuickCurve(parent) {}

    enum ArcDirection { Clockwise, Counterclockwise };
    Q_ENUM(ArcDirection)

    qreal radiusX() const;
    void setRadiusX(qreal);

    qreal radiusY() const;
    void setRadiusY(qreal);

    bool useLargeArc() const;
    void setUseLargeArc(bool);

    ArcDirection direction() const;
    void setDirection(ArcDirection direction);

    qreal xAxisRotation() const;
    void setXAxisRotation(qreal rotation);

    void addToPath(QPainterPath &path, const QQuickPathData &) override;

Q_SIGNALS:
    void radiusXChanged();
    void radiusYChanged();
    void useLargeArcChanged();
    void directionChanged();
    Q_REVISION(2) void xAxisRotationChanged();

private:
    qreal _radiusX = 0;
    qreal _radiusY = 0;
    bool _useLargeArc = false;
    ArcDirection _direction = Clockwise;
    qreal _xAxisRotation = 0;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathAngleArc : public QQuickCurve
{
    Q_OBJECT
    Q_PROPERTY(qreal centerX READ centerX WRITE setCenterX NOTIFY centerXChanged)
    Q_PROPERTY(qreal centerY READ centerY WRITE setCenterY NOTIFY centerYChanged)
    Q_PROPERTY(qreal radiusX READ radiusX WRITE setRadiusX NOTIFY radiusXChanged)
    Q_PROPERTY(qreal radiusY READ radiusY WRITE setRadiusY NOTIFY radiusYChanged)
    Q_PROPERTY(qreal startAngle READ startAngle WRITE setStartAngle NOTIFY startAngleChanged)
    Q_PROPERTY(qreal sweepAngle READ sweepAngle WRITE setSweepAngle NOTIFY sweepAngleChanged)
    Q_PROPERTY(bool moveToStart READ moveToStart WRITE setMoveToStart NOTIFY moveToStartChanged)

public:
    QQuickPathAngleArc(QObject *parent=nullptr)
        : QQuickCurve(parent) {}

    qreal centerX() const;
    void setCenterX(qreal);

    qreal centerY() const;
    void setCenterY(qreal);

    qreal radiusX() const;
    void setRadiusX(qreal);

    qreal radiusY() const;
    void setRadiusY(qreal);

    qreal startAngle() const;
    void setStartAngle(qreal);

    qreal sweepAngle() const;
    void setSweepAngle(qreal);

    bool moveToStart() const;
    void setMoveToStart(bool);

    void addToPath(QPainterPath &path, const QQuickPathData &) override;

Q_SIGNALS:
    void centerXChanged();
    void centerYChanged();
    void radiusXChanged();
    void radiusYChanged();
    void startAngleChanged();
    void sweepAngleChanged();
    void moveToStartChanged();

private:
    qreal _centerX = 0;
    qreal _centerY = 0;
    qreal _radiusX = 0;
    qreal _radiusY = 0;
    qreal _startAngle = 0;
    qreal _sweepAngle = 0;
    bool _moveToStart = true;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathSvg : public QQuickCurve
{
    Q_OBJECT
    Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged)
public:
    QQuickPathSvg(QObject *parent=nullptr) : QQuickCurve(parent) {}

    QString path() const;
    void setPath(const QString &path);

    void addToPath(QPainterPath &path, const QQuickPathData &) override;

Q_SIGNALS:
    void pathChanged();

private:
    QString _path;
};

class Q_QUICK_PRIVATE_EXPORT QQuickPathPercent : public QQuickPathElement
{
    Q_OBJECT
    Q_PROPERTY(qreal value READ value WRITE setValue NOTIFY valueChanged)
public:
    QQuickPathPercent(QObject *parent=nullptr) : QQuickPathElement(parent) {}

    qreal value() const;
    void setValue(qreal value);

Q_SIGNALS:
    void valueChanged();

private:
    qreal _value = 0;
};

struct QQuickCachedBezier
{
    QQuickCachedBezier() {}
    QBezier bezier;
    int element;
    qreal bezLength;
    qreal currLength;
    qreal p;
    bool isValid = false;
};

class QQuickPathPrivate;
class Q_QUICK_PRIVATE_EXPORT QQuickPath : public QObject, public QQmlParserStatus
{
    Q_OBJECT

    Q_INTERFACES(QQmlParserStatus)
    Q_PROPERTY(QQmlListProperty<QQuickPathElement> pathElements READ pathElements)
    Q_PROPERTY(qreal startX READ startX WRITE setStartX NOTIFY startXChanged)
    Q_PROPERTY(qreal startY READ startY WRITE setStartY NOTIFY startYChanged)
    Q_PROPERTY(bool closed READ isClosed NOTIFY changed)
    Q_CLASSINFO("DefaultProperty", "pathElements")
    Q_INTERFACES(QQmlParserStatus)
public:
    QQuickPath(QObject *parent=nullptr);
    ~QQuickPath() override;

    QQmlListProperty<QQuickPathElement> pathElements();

    qreal startX() const;
    void setStartX(qreal x);
    bool hasStartX() const;

    qreal startY() const;
    void setStartY(qreal y);
    bool hasStartY() const;

    bool isClosed() const;

    QPainterPath path() const;
    QStringList attributes() const;
    qreal attributeAt(const QString &, qreal) const;
    QPointF pointAt(qreal) const;
    QPointF sequentialPointAt(qreal p, qreal *angle = nullptr) const;
    void invalidateSequentialHistory() const;

Q_SIGNALS:
    void changed();
    void startXChanged();
    void startYChanged();

protected:
    QQuickPath(QQuickPathPrivate &dd, QObject *parent = nullptr);
    void componentComplete() override;
    void classBegin() override;
    void disconnectPathElements();
    void connectPathElements();
    void gatherAttributes();

    // pathElements property
    static QQuickPathElement *pathElements_at(QQmlListProperty<QQuickPathElement> *, int);
    static void pathElements_append(QQmlListProperty<QQuickPathElement> *, QQuickPathElement *);
    static int pathElements_count(QQmlListProperty<QQuickPathElement> *);
    static void pathElements_clear(QQmlListProperty<QQuickPathElement> *);

private Q_SLOTS:
    void processPath();

private:
    struct AttributePoint {
        AttributePoint() {}
        AttributePoint(const AttributePoint &other)
            : percent(other.percent), scale(other.scale), origpercent(other.origpercent), values(other.values) {}
        AttributePoint &operator=(const AttributePoint &other) {
            percent = other.percent; scale = other.scale; origpercent = other.origpercent; values = other.values; return *this;
        }
        qreal percent = 0;      //massaged percent along the painter path
        qreal scale = 1;
        qreal origpercent = 0;  //'real' percent along the painter path
        QHash<QString, qreal> values;
    };

    void interpolate(int idx, const QString &name, qreal value);
    void endpoint(const QString &name);
    void createPointCache() const;

    static void interpolate(QList<AttributePoint> &points, int idx, const QString &name, qreal value);
    static void endpoint(QList<AttributePoint> &attributePoints, const QString &name);
    static QPointF forwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = nullptr);
    static QPointF backwardsPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = nullptr);

private:
    Q_DISABLE_COPY(QQuickPath)
    Q_DECLARE_PRIVATE(QQuickPath)
    friend class QQuickPathAnimationUpdater;

public:
    QPainterPath createPath(const QPointF &startPoint, const QPointF &endPoint, const QStringList &attributes, qreal &pathLength, QList<AttributePoint> &attributePoints, bool *closed = nullptr);
    QPainterPath createShapePath(const QPointF &startPoint, const QPointF &endPoint, qreal &pathLength, bool *closed = nullptr);
    static QPointF sequentialPointAt(const QPainterPath &path, const qreal &pathLength, const QList<AttributePoint> &attributePoints, QQuickCachedBezier &prevBez, qreal p, qreal *angle = nullptr);
};

QT_END_NAMESPACE

QML_DECLARE_TYPE(QQuickPathElement)
QML_DECLARE_TYPE(QQuickPathAttribute)
QML_DECLARE_TYPE(QQuickCurve)
QML_DECLARE_TYPE(QQuickPathLine)
QML_DECLARE_TYPE(QQuickPathMove)
QML_DECLARE_TYPE(QQuickPathQuad)
QML_DECLARE_TYPE(QQuickPathCubic)
QML_DECLARE_TYPE(QQuickPathCatmullRomCurve)
QML_DECLARE_TYPE(QQuickPathArc)
QML_DECLARE_TYPE(QQuickPathAngleArc)
QML_DECLARE_TYPE(QQuickPathSvg)
QML_DECLARE_TYPE(QQuickPathPercent)
QML_DECLARE_TYPE(QQuickPath)

#endif // QQUICKPATH_H
