/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt Data Visualization module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:GPL$
** 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 General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 or (at your option) 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.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-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qitemmodelsurfacedataproxy_p.h"
#include "surfaceitemmodelhandler_p.h"

QT_BEGIN_NAMESPACE_DATAVISUALIZATION

/*!
 * \class QItemModelSurfaceDataProxy
 * \inmodule QtDataVisualization
 * \brief Proxy class for presenting data in item models with Q3DSurface.
 * \since QtDataVisualization 1.0
 *
 * QItemModelSurfaceDataProxy allows you to use QAbstractItemModel derived models as a data source
 * for Q3DSurface. It uses the defined mappings to map data from the model to rows, columns, and
 * surface points of Q3DSurface graph.
 *
 * Data is resolved asynchronously whenever the mapping or the model changes.
 * QSurfaceDataProxy::arrayReset() is emitted when the data has been resolved.
 * However, when useModelCategories property is set to true, single item changes are resolved
 * synchronously, unless the same frame also contains a change that causes the whole model to be
 * resolved.
 *
 * There are three ways to use mappings:
 *
 * 1) If useModelCategories property is set to true, this proxy will map rows and
 *    columns of QAbstractItemModel to rows and columns of Q3DSurface, and uses the value returned for
 *    Qt::DisplayRole as Y-position by default. Row and column headers are used for Z-position and
 *    X-position by default, if they can be converted to floats. Otherwise row and column indices
 *    are used.
 *    The Y-position role to be used can be redefined if Qt::DisplayRole is not suitable.
 *    The Z-position and X-position roles to be used can be redefined if the headers or indices
 *    are not suitable.
 *
 * 2) For models that do not have data already neatly sorted into rows and columns, such as
 *    QAbstractListModel based models, you can define a role from the model to map for each of row,
 *    column and Y-position.
 *
 * 3) If you do not want to include all data contained in the model, or the autogenerated rows and
 *    columns are not ordered as you wish, you can specify which rows and columns should be included
 *    and in which order by defining an explicit list of categories for either or both of rows and
 *    columns.
 *
 * For example, assume that you have a custom QAbstractItemModel storing surface topography data.
 * Each item in the model has the roles "longitude", "latitude", and "height".
 * The item model already contains the data properly sorted so that longitudes and latitudes are
 * first encountered in correct order, which enables us to utilize the row and column category
 * autogeneration.
 * You could do the following to display the data in a surface graph:
 *
 * \snippet doc_src_qtdatavisualization.cpp 5
 *
 * If the fields of the model do not contain the data in the exact format you need, you can specify
 * a search pattern regular expression and a replace rule for each role to get the value in a
 * format you need. For more information how the replace using regular expressions works, see
 * QString::replace(const QRegExp &rx, const QString &after) function documentation. Note that
 * using regular expressions has an impact on the performance, so it's more efficient to utilize
 * item models where doing search and replace is not necessary to get the desired values.
 *
 * For example about using the search patterns in conjunction with the roles, see
 * ItemModelBarDataProxy usage in \l{Qt Quick 2 Bars Example}.
 *
 * \sa {Qt Data Visualization Data Handling}
 */

/*!
 * \qmltype ItemModelSurfaceDataProxy
 * \inqmlmodule QtDataVisualization
 * \since QtDataVisualization 1.0
 * \ingroup datavisualization_qml
 * \instantiates QItemModelSurfaceDataProxy
 * \inherits SurfaceDataProxy
 * \brief Proxy class for presenting data in item models with Surface3D.
 *
 * This type allows you to use AbstractItemModel derived models as a data source for Surface3D.
 *
 * Data is resolved asynchronously whenever the mapping or the model changes.
 * QSurfaceDataProxy::arrayReset() is emitted when the data has been resolved.
 *
 * For ItemModelSurfaceDataProxy enums, see \l{QItemModelSurfaceDataProxy::MultiMatchBehavior}.
 *
 * For more details, see QItemModelSurfaceDataProxy documentation.
 *
 * Usage example:
 *
 * \snippet doc_src_qmldatavisualization.cpp 9
 *
 * \sa SurfaceDataProxy, {Qt Data Visualization Data Handling}
 */

/*!
 * \qmlproperty model ItemModelSurfaceDataProxy::itemModel
 * The item model.
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::rowRole
 * Defines the item model role to map into row category.
 * In addition to defining which row the data belongs to, the value indicated by row role
 * is also set as the Z-coordinate value of the QSurfaceDataItem when model data is resolved,
 * unless a separate zPos role is also defined.
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::columnRole
 * Defines the item model role to map into column category.
 * In addition to defining which column the data belongs to, the value indicated by column role
 * is also set as the X-coordinate value of the QSurfaceDataItem when model data is resolved,
 * unless a separate xPos role is also defined.
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::xPosRole
 * Defines the item model role to map into X position. If this role is not defined, columnRole is
 * used to determine the X-coordinate value of resolved \l{QSurfaceDataItem}s.
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::yPosRole
 * Defines the item model role to map into Y position.
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::zPosRole
 * Defines the item model role to map into Z position. If this role is not defined, rowRole is
 * used to determine the Z-coordinate value of resolved \l{QSurfaceDataItem}s.
 */

/*!
 * \qmlproperty list<String> ItemModelSurfaceDataProxy::rowCategories
 * The row categories of the mapping. Only items with row roles that are found in this list are
 * included when data is resolved. The rows are ordered in the same order as they are in this list.
 */

/*!
 * \qmlproperty list<String> ItemModelSurfaceDataProxy::columnCategories
 * The column categories of the mapping. Only items with column roles that are found in this
 * list are included when data is resolved. The columns are ordered in the same order as they are
 * in this list.
 */

/*!
 * \qmlproperty bool ItemModelSurfaceDataProxy::useModelCategories
 * When set to true, the mapping ignores row and column roles and categories, and uses
 * the rows and columns from the model instead. Defaults to \c{false}.
 */

/*!
 * \qmlproperty bool ItemModelSurfaceDataProxy::autoRowCategories
 * When set to true, the mapping ignores any explicitly set row categories
 * and overwrites them with automatically generated ones whenever the
 * data from model is resolved. Proxy minimum and maximum row values are also
 * autogenerated from data when this is set to true. Defaults to \c{true}.
 */

/*!
 * \qmlproperty bool ItemModelSurfaceDataProxy::autoColumnCategories
 * When set to true, the mapping ignores any explicitly set column categories
 * and overwrites them with automatically generated ones whenever the
 * data from model is resolved. Proxy minimum and maximum column values are also
 * autogenerated from data when this is set to true. Defaults to \c{true}.
 */

/*!
 * \qmlproperty regExp ItemModelSurfaceDataProxy::rowRolePattern
 *
 * When set, a search and replace is done on the value mapped by row role before it is used as
 * a row category. This property specifies the regular expression to find the portion of the
 * mapped value to replace and rowRoleReplace property contains the replacement string.
 *
 * \sa rowRole, rowRoleReplace
 */

/*!
 * \qmlproperty regExp ItemModelSurfaceDataProxy::columnRolePattern
 *
 * When set, a search and replace is done on the value mapped by column role before it is used
 * as a column category. This property specifies the regular expression to find the portion of the
 * mapped value to replace and columnRoleReplace property contains the replacement string.
 *
 * \sa columnRole, columnRoleReplace
 */

/*!
 * \qmlproperty regExp ItemModelSurfaceDataProxy::xPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by xPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and xPosRoleReplace property contains the replacement string.
 *
 * \sa xPosRole, xPosRoleReplace
 */

/*!
 * \qmlproperty regExp ItemModelSurfaceDataProxy::yPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by yPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and yPosRoleReplace property contains the replacement string.
 *
 * \sa yPosRole, yPosRoleReplace
 */

/*!
 * \qmlproperty regExp ItemModelSurfaceDataProxy::zPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by zPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and zPosRoleReplace property contains the replacement string.
 *
 * \sa zPosRole, zPosRoleReplace
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::rowRoleReplace
 *
 * This property defines the replace content to be used in conjunction with rowRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa rowRole, rowRolePattern
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::columnRoleReplace
 *
 * This property defines the replace content to be used in conjunction with columnRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa columnRole, columnRolePattern
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::xPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with xPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa xPosRole, xPosRolePattern
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::yPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with yPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa yPosRole, yPosRolePattern
 */

/*!
 * \qmlproperty string ItemModelSurfaceDataProxy::zPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with zPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa zPosRole, zPosRolePattern
 */

/*!
 * \qmlproperty ItemModelSurfaceDataProxy.MultiMatchBehavior ItemModelSurfaceDataProxy::multiMatchBehavior
 * This property defines how multiple matches for each row/column combination are handled.
 * Defaults to \l{QItemModelSurfaceDataProxy::MMBLast}{ItemModelSurfaceDataProxy.MMBLast}.
 *
 * For example, you might have an item model with timestamped data taken at irregular intervals
 * and you want to visualize an average position of data items on each hour with a surface graph.
 * This can be done by specifying row and column categories so that each surface point represents
 * an hour, and setting multiMatchBehavior to
 * \l{QItemModelSurfaceDataProxy::MMBAverage}{ItemModelSurfaceDataProxy.MMBAverage}.
 */

/*!
 *  \enum QItemModelSurfaceDataProxy::MultiMatchBehavior
 *
 *  Behavior types for QItemModelSurfaceDataProxy::multiMatchBehavior property.
 *
 *  \value MMBFirst
 *         The position values are taken from the first item in the item model that matches
 *         each row/column combination.
 *  \value MMBLast
 *         The position values are taken from the last item in the item model that matches
 *         each row/column combination.
 *  \value MMBAverage
 *         The position values from all items matching each row/column combination are
 *         averaged together and the averages are used as the surface point position.
 *  \value MMBCumulativeY
 *         For X and Z values this acts just like \c{MMBAverage}, but Y values are added together
 *         instead of averaged and the total is used as the surface point Y position.
 */

/*!
 * Constructs QItemModelSurfaceDataProxy with optional \a parent.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 * The yPosRole role is set to \a yPosRole.
 * This constructor is meant to be used with models that have data properly sorted
 * in rows and columns already, so it also sets useModelCategories property to true.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       const QString &yPosRole,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->m_yPosRole = yPosRole;
    dptr()->m_useModelCategories = true;
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 * The role mappings are set with \a rowRole, \a columnRole, and \a yPosRole.
 * The zPosRole and the xPosRole are set to \a rowRole and \a columnRole, respectively.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       const QString &rowRole,
                                                       const QString &columnRole,
                                                       const QString &yPosRole,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->m_rowRole = rowRole;
    dptr()->m_columnRole = columnRole;
    dptr()->m_xPosRole = columnRole;
    dptr()->m_yPosRole = yPosRole;
    dptr()->m_zPosRole = rowRole;
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 * The role mappings are set with \a rowRole, \a columnRole, \a xPosRole, \a yPosRole, and
 * \a zPosRole.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       const QString &rowRole,
                                                       const QString &columnRole,
                                                       const QString &xPosRole,
                                                       const QString &yPosRole,
                                                       const QString &zPosRole,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->m_rowRole = rowRole;
    dptr()->m_columnRole = columnRole;
    dptr()->m_xPosRole = xPosRole;
    dptr()->m_yPosRole = yPosRole;
    dptr()->m_zPosRole = zPosRole;
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 * The role mappings are set with \a rowRole, \a columnRole, and \a yPosRole.
 * The zPosRole and the xPosRole are set to \a rowRole and \a columnRole, respectively.
 * Row and column categories are set with \a rowCategories and \a columnCategories.
 * This constructor also sets autoRowCategories and autoColumnCategories to false.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       const QString &rowRole,
                                                       const QString &columnRole,
                                                       const QString &yPosRole,
                                                       const QStringList &rowCategories,
                                                       const QStringList &columnCategories,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->m_rowRole = rowRole;
    dptr()->m_columnRole = columnRole;
    dptr()->m_xPosRole = columnRole;
    dptr()->m_yPosRole = yPosRole;
    dptr()->m_zPosRole = rowRole;
    dptr()->m_rowCategories = rowCategories;
    dptr()->m_columnCategories = columnCategories;
    dptr()->m_autoRowCategories = false;
    dptr()->m_autoColumnCategories = false;
    dptr()->connectItemModelHandler();
}

/*!
 * Constructs QItemModelSurfaceDataProxy with \a itemModel and optional \a parent. Proxy doesn't take
 * ownership of the \a itemModel, as typically item models are owned by other controls.
 * The role mappings are set with \a rowRole, \a columnRole, \a xPosRole, \a yPosRole,
 * and \a zPosRole.
 * Row and column categories are set with \a rowCategories and \a columnCategories.
 * This constructor also sets autoRowCategories and autoColumnCategories to false.
 */
QItemModelSurfaceDataProxy::QItemModelSurfaceDataProxy(QAbstractItemModel *itemModel,
                                                       const QString &rowRole,
                                                       const QString &columnRole,
                                                       const QString &xPosRole,
                                                       const QString &yPosRole,
                                                       const QString &zPosRole,
                                                       const QStringList &rowCategories,
                                                       const QStringList &columnCategories,
                                                       QObject *parent)
    : QSurfaceDataProxy(new QItemModelSurfaceDataProxyPrivate(this), parent)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
    dptr()->m_rowRole = rowRole;
    dptr()->m_columnRole = columnRole;
    dptr()->m_xPosRole = xPosRole;
    dptr()->m_yPosRole = yPosRole;
    dptr()->m_zPosRole = zPosRole;
    dptr()->m_rowCategories = rowCategories;
    dptr()->m_columnCategories = columnCategories;
    dptr()->m_autoRowCategories = false;
    dptr()->m_autoColumnCategories = false;
    dptr()->connectItemModelHandler();
}

/*!
 * Destroys QItemModelSurfaceDataProxy.
 */
QItemModelSurfaceDataProxy::~QItemModelSurfaceDataProxy()
{
}

/*!
 * \property QItemModelSurfaceDataProxy::itemModel
 *
 * Defines item model. Does not take ownership of the model, but does connect to it to listen for
 * changes.
 */
void QItemModelSurfaceDataProxy::setItemModel(QAbstractItemModel *itemModel)
{
    dptr()->m_itemModelHandler->setItemModel(itemModel);
}

QAbstractItemModel *QItemModelSurfaceDataProxy::itemModel() const
{
    return dptrc()->m_itemModelHandler->itemModel();
}

/*!
 * \property QItemModelSurfaceDataProxy::rowRole
 *
 * Defines the item model role to map into row category.
 * In addition to defining which row the data belongs to, the value indicated by row role
 * is also set as the Z-coordinate value of the QSurfaceDataItem when model data is resolved,
 * unless a separate zPos role is also defined.
 */
void QItemModelSurfaceDataProxy::setRowRole(const QString &role)
{
    if (dptr()->m_rowRole != role) {
        dptr()->m_rowRole = role;
        emit rowRoleChanged(role);
    }
}

QString QItemModelSurfaceDataProxy::rowRole() const
{
    return dptrc()->m_rowRole;
}

/*!
 * \property QItemModelSurfaceDataProxy::columnRole
 *
 * Defines the item model role to map into column category.
 * In addition to defining which column the data belongs to, the value indicated by column role
 * is also set as the X-coordinate value of the QSurfaceDataItem when model data is resolved,
 * unless a separate xPos role is also defined.
 */
void QItemModelSurfaceDataProxy::setColumnRole(const QString &role)
{
    if (dptr()->m_columnRole != role) {
        dptr()->m_columnRole = role;
        emit columnRoleChanged(role);
    }
}

QString QItemModelSurfaceDataProxy::columnRole() const
{
    return dptrc()->m_columnRole;
}

/*!
 * \property QItemModelSurfaceDataProxy::xPosRole
 *
 * Defines the item model role to map into X position. If this role is not defined, columnRole is
 * used to determine the X-coordinate value of resolved \l{QSurfaceDataItem}s.
 */
void QItemModelSurfaceDataProxy::setXPosRole(const QString &role)
{
    if (dptr()->m_xPosRole != role) {
        dptr()->m_xPosRole = role;
        emit xPosRoleChanged(role);
    }
}

QString QItemModelSurfaceDataProxy::xPosRole() const
{
    return dptrc()->m_xPosRole;
}

/*!
 * \property QItemModelSurfaceDataProxy::yPosRole
 *
 * Defines the item model role to map into Y position.
 */
void QItemModelSurfaceDataProxy::setYPosRole(const QString &role)
{
    if (dptr()->m_yPosRole != role) {
        dptr()->m_yPosRole = role;
        emit yPosRoleChanged(role);
    }
}

QString QItemModelSurfaceDataProxy::yPosRole() const
{
    return dptrc()->m_yPosRole;
}

/*!
 * \property QItemModelSurfaceDataProxy::zPosRole
 *
 * Defines the item model role to map into Z position. If this role is not defined, rowRole is
 * used to determine the Z-coordinate value of resolved \l{QSurfaceDataItem}s.
 */
void QItemModelSurfaceDataProxy::setZPosRole(const QString &role)
{
    if (dptr()->m_zPosRole != role) {
        dptr()->m_zPosRole = role;
        emit zPosRoleChanged(role);
    }
}

QString QItemModelSurfaceDataProxy::zPosRole() const
{
    return dptrc()->m_zPosRole;
}

/*!
 * \property QItemModelSurfaceDataProxy::rowCategories
 *
 * Defines the row \a categories for the mapping.
 */
void QItemModelSurfaceDataProxy::setRowCategories(const QStringList &categories)
{
    if (dptr()->m_rowCategories != categories) {
        dptr()->m_rowCategories = categories;
        emit rowCategoriesChanged();
    }
}

QStringList QItemModelSurfaceDataProxy::rowCategories() const
{
    return dptrc()->m_rowCategories;
}

/*!
 * \property QItemModelSurfaceDataProxy::columnCategories
 *
 * Defines the column \a categories for the mapping.
 */
void QItemModelSurfaceDataProxy::setColumnCategories(const QStringList &categories)
{
    if (dptr()->m_columnCategories != categories) {
        dptr()->m_columnCategories = categories;
        emit columnCategoriesChanged();
    }
}

QStringList QItemModelSurfaceDataProxy::columnCategories() const
{
    return dptrc()->m_columnCategories;
}

/*!
 * \property QItemModelSurfaceDataProxy::useModelCategories
 *
 * When set to true, the mapping ignores row and column roles and categories, and uses
 * the rows and columns from the model instead. Defaults to \c{false}.
 */
void QItemModelSurfaceDataProxy::setUseModelCategories(bool enable)
{
    if (dptr()->m_useModelCategories != enable) {
        dptr()->m_useModelCategories = enable;
        emit useModelCategoriesChanged(enable);
    }
}

bool QItemModelSurfaceDataProxy::useModelCategories() const
{
    return dptrc()->m_useModelCategories;
}

/*!
 * \property QItemModelSurfaceDataProxy::autoRowCategories
 *
 * When set to true, the mapping ignores any explicitly set row categories
 * and overwrites them with automatically generated ones whenever the
 * data from model is resolved. Defaults to \c{true}.
 */
void QItemModelSurfaceDataProxy::setAutoRowCategories(bool enable)
{
    if (dptr()->m_autoRowCategories != enable) {
        dptr()->m_autoRowCategories = enable;
        emit autoRowCategoriesChanged(enable);
    }
}

bool QItemModelSurfaceDataProxy::autoRowCategories() const
{
    return dptrc()->m_autoRowCategories;
}

/*!
 * \property QItemModelSurfaceDataProxy::autoColumnCategories
 *
 * When set to true, the mapping ignores any explicitly set column categories
 * and overwrites them with automatically generated ones whenever the
 * data from model is resolved. Defaults to \c{true}.
 */
void QItemModelSurfaceDataProxy::setAutoColumnCategories(bool enable)
{
    if (dptr()->m_autoColumnCategories != enable) {
        dptr()->m_autoColumnCategories = enable;
        emit autoColumnCategoriesChanged(enable);
    }
}

bool QItemModelSurfaceDataProxy::autoColumnCategories() const
{
    return dptrc()->m_autoColumnCategories;
}

/*!
 * Changes \a rowRole, \a columnRole, \a xPosRole, \a yPosRole, \a zPosRole,
 * \a rowCategories and \a columnCategories to the mapping.
 */
void QItemModelSurfaceDataProxy::remap(const QString &rowRole,
                                       const QString &columnRole,
                                       const QString &xPosRole,
                                       const QString &yPosRole,
                                       const QString &zPosRole,
                                       const QStringList &rowCategories,
                                       const QStringList &columnCategories)
{
    setRowRole(rowRole);
    setColumnRole(columnRole);
    setXPosRole(xPosRole);
    setYPosRole(yPosRole);
    setZPosRole(zPosRole);
    setRowCategories(rowCategories);
    setColumnCategories(columnCategories);
}

/*!
 * \return index of the specified \a category in row categories list.
 * If the row categories list is empty, -1 is returned.
 * \note If the automatic row categories generation is in use, this method will
 * not return a valid index before the data in the model is resolved for the first time.
 */
int QItemModelSurfaceDataProxy::rowCategoryIndex(const QString &category)
{
    return dptr()->m_rowCategories.indexOf(category);
}

/*!
 * \return index of the specified \a category in column categories list.
 * If the category is not found, -1 is returned.
 * \note If the automatic column categories generation is in use, this method will
 * not return a valid index before the data in the model is resolved for the first time.
 */
int QItemModelSurfaceDataProxy::columnCategoryIndex(const QString &category)
{
    return dptr()->m_columnCategories.indexOf(category);
}

/*!
 * \property QItemModelSurfaceDataProxy::rowRolePattern
 *
 * When set, a search and replace is done on the value mapped by row role before it is used as
 * a row category. This property specifies the regular expression to find the portion of the
 * mapped value to replace and rowRoleReplace property contains the replacement string.
 *
 * \sa rowRole, rowRoleReplace
 */
void QItemModelSurfaceDataProxy::setRowRolePattern(const QRegExp &pattern)
{
    if (dptr()->m_rowRolePattern != pattern) {
        dptr()->m_rowRolePattern = pattern;
        emit rowRolePatternChanged(pattern);
    }
}

QRegExp QItemModelSurfaceDataProxy::rowRolePattern() const
{
    return dptrc()->m_rowRolePattern;
}

/*!
 * \property QItemModelSurfaceDataProxy::columnRolePattern
 *
 * When set, a search and replace is done on the value mapped by column role before it is used
 * as a column category. This property specifies the regular expression to find the portion of the
 * mapped value to replace and columnRoleReplace property contains the replacement string.
 *
 * \sa columnRole, columnRoleReplace
 */
void QItemModelSurfaceDataProxy::setColumnRolePattern(const QRegExp &pattern)
{
    if (dptr()->m_columnRolePattern != pattern) {
        dptr()->m_columnRolePattern = pattern;
        emit columnRolePatternChanged(pattern);
    }
}

QRegExp QItemModelSurfaceDataProxy::columnRolePattern() const
{
    return dptrc()->m_columnRolePattern;
}

/*!
 * \property QItemModelSurfaceDataProxy::xPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by xPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and xPosRoleReplace property contains the replacement string.
 *
 * \sa xPosRole, xPosRoleReplace
 */
void QItemModelSurfaceDataProxy::setXPosRolePattern(const QRegExp &pattern)
{
    if (dptr()->m_xPosRolePattern != pattern) {
        dptr()->m_xPosRolePattern = pattern;
        emit xPosRolePatternChanged(pattern);
    }
}

QRegExp QItemModelSurfaceDataProxy::xPosRolePattern() const
{
    return dptrc()->m_xPosRolePattern;
}

/*!
 * \property QItemModelSurfaceDataProxy::yPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by yPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and yPosRoleReplace property contains the replacement string.
 *
 * \sa yPosRole, yPosRoleReplace
 */
void QItemModelSurfaceDataProxy::setYPosRolePattern(const QRegExp &pattern)
{
    if (dptr()->m_yPosRolePattern != pattern) {
        dptr()->m_yPosRolePattern = pattern;
        emit yPosRolePatternChanged(pattern);
    }
}

QRegExp QItemModelSurfaceDataProxy::yPosRolePattern() const
{
    return dptrc()->m_yPosRolePattern;
}

/*!
 * \property QItemModelSurfaceDataProxy::zPosRolePattern
 *
 * When set, a search and replace is done on the value mapped by zPos role before it is used as
 * a item position value. This property specifies the regular expression to find the portion of the
 * mapped value to replace and zPosRoleReplace property contains the replacement string.
 *
 * \sa zPosRole, zPosRoleReplace
 */
void QItemModelSurfaceDataProxy::setZPosRolePattern(const QRegExp &pattern)
{
    if (dptr()->m_zPosRolePattern != pattern) {
        dptr()->m_zPosRolePattern = pattern;
        emit zPosRolePatternChanged(pattern);
    }
}

QRegExp QItemModelSurfaceDataProxy::zPosRolePattern() const
{
    return dptrc()->m_zPosRolePattern;
}

/*!
 * \property QItemModelSurfaceDataProxy::rowRoleReplace
 *
 * This property defines the replace content to be used in conjunction with rowRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa rowRole, rowRolePattern
 */
void QItemModelSurfaceDataProxy::setRowRoleReplace(const QString &replace)
{
    if (dptr()->m_rowRoleReplace != replace) {
        dptr()->m_rowRoleReplace = replace;
        emit rowRoleReplaceChanged(replace);
    }
}

QString QItemModelSurfaceDataProxy::rowRoleReplace() const
{
    return dptrc()->m_rowRoleReplace;
}

/*!
 * \property QItemModelSurfaceDataProxy::columnRoleReplace
 *
 * This property defines the replace content to be used in conjunction with columnRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa columnRole, columnRolePattern
 */
void QItemModelSurfaceDataProxy::setColumnRoleReplace(const QString &replace)
{
    if (dptr()->m_columnRoleReplace != replace) {
        dptr()->m_columnRoleReplace = replace;
        emit columnRoleReplaceChanged(replace);
    }
}

QString QItemModelSurfaceDataProxy::columnRoleReplace() const
{
    return dptrc()->m_columnRoleReplace;
}

/*!
 * \property QItemModelSurfaceDataProxy::xPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with xPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa xPosRole, xPosRolePattern
 */
void QItemModelSurfaceDataProxy::setXPosRoleReplace(const QString &replace)
{
    if (dptr()->m_xPosRoleReplace != replace) {
        dptr()->m_xPosRoleReplace = replace;
        emit xPosRoleReplaceChanged(replace);
    }
}

QString QItemModelSurfaceDataProxy::xPosRoleReplace() const
{
    return dptrc()->m_xPosRoleReplace;
}

/*!
 * \property QItemModelSurfaceDataProxy::yPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with yPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa yPosRole, yPosRolePattern
 */
void QItemModelSurfaceDataProxy::setYPosRoleReplace(const QString &replace)
{
    if (dptr()->m_yPosRoleReplace != replace) {
        dptr()->m_yPosRoleReplace = replace;
        emit yPosRoleReplaceChanged(replace);
    }
}

QString QItemModelSurfaceDataProxy::yPosRoleReplace() const
{
    return dptrc()->m_yPosRoleReplace;
}

/*!
 * \property QItemModelSurfaceDataProxy::zPosRoleReplace
 *
 * This property defines the replace content to be used in conjunction with zPosRolePattern.
 * Defaults to empty string. For more information on how the search and replace using regular
 * expressions works, see QString::replace(const QRegExp &rx, const QString &after)
 * function documentation.
 *
 * \sa zPosRole, zPosRolePattern
 */
void QItemModelSurfaceDataProxy::setZPosRoleReplace(const QString &replace)
{
    if (dptr()->m_zPosRoleReplace != replace) {
        dptr()->m_zPosRoleReplace = replace;
        emit zPosRoleReplaceChanged(replace);
    }
}

QString QItemModelSurfaceDataProxy::zPosRoleReplace() const
{
    return dptrc()->m_zPosRoleReplace;
}

/*!
 * \property QItemModelSurfaceDataProxy::multiMatchBehavior
 *
 * This property defines how multiple matches for each row/column combination are handled.
 * Defaults to QItemModelSurfaceDataProxy::MMBLast.
 *
 * For example, you might have an item model with timestamped data taken at irregular intervals
 * and you want to visualize an average position of data items on each hour with a surface graph.
 * This can be done by specifying row and column categories so that each surface point represents
 * an hour, and setting multiMatchBehavior to QItemModelSurfaceDataProxy::MMBAverage.
 */

void QItemModelSurfaceDataProxy::setMultiMatchBehavior(QItemModelSurfaceDataProxy::MultiMatchBehavior behavior)
{
    if (dptr()->m_multiMatchBehavior != behavior) {
        dptr()->m_multiMatchBehavior = behavior;
        emit multiMatchBehaviorChanged(behavior);
    }
}

QItemModelSurfaceDataProxy::MultiMatchBehavior QItemModelSurfaceDataProxy::multiMatchBehavior() const
{
    return dptrc()->m_multiMatchBehavior;
}

/*!
 * \internal
 */
QItemModelSurfaceDataProxyPrivate *QItemModelSurfaceDataProxy::dptr()
{
    return static_cast<QItemModelSurfaceDataProxyPrivate *>(d_ptr.data());
}

/*!
 * \internal
 */
const QItemModelSurfaceDataProxyPrivate *QItemModelSurfaceDataProxy::dptrc() const
{
    return static_cast<const QItemModelSurfaceDataProxyPrivate *>(d_ptr.data());
}

// QItemModelSurfaceDataProxyPrivate

QItemModelSurfaceDataProxyPrivate::QItemModelSurfaceDataProxyPrivate(QItemModelSurfaceDataProxy *q)
    : QSurfaceDataProxyPrivate(q),
      m_itemModelHandler(new SurfaceItemModelHandler(q)),
      m_useModelCategories(false),
      m_autoRowCategories(true),
      m_autoColumnCategories(true),
      m_multiMatchBehavior(QItemModelSurfaceDataProxy::MMBLast)
{
}

QItemModelSurfaceDataProxyPrivate::~QItemModelSurfaceDataProxyPrivate()
{
    delete m_itemModelHandler;
}

QItemModelSurfaceDataProxy *QItemModelSurfaceDataProxyPrivate::qptr()
{
    return static_cast<QItemModelSurfaceDataProxy *>(q_ptr);
}

void QItemModelSurfaceDataProxyPrivate::connectItemModelHandler()
{
    QObject::connect(m_itemModelHandler, &SurfaceItemModelHandler::itemModelChanged,
                     qptr(), &QItemModelSurfaceDataProxy::itemModelChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowRoleChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnRoleChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::xPosRoleChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::yPosRoleChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::zPosRoleChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowCategoriesChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnCategoriesChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::useModelCategoriesChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::autoRowCategoriesChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::autoColumnCategoriesChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowRolePatternChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnRolePatternChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::xPosRolePatternChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::yPosRolePatternChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::zPosRolePatternChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::rowRoleReplaceChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::columnRoleReplaceChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::xPosRoleReplaceChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::yPosRoleReplaceChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::zPosRoleReplaceChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
    QObject::connect(qptr(), &QItemModelSurfaceDataProxy::multiMatchBehaviorChanged,
                     m_itemModelHandler, &AbstractItemModelHandler::handleMappingChanged);
}

QT_END_NAMESPACE_DATAVISUALIZATION
