/****************************************************************************
 ** Copyright (C) 2001-2006 Klarälvdalens Datakonsult AB.  All rights reserved.
 **
 ** This file is part of the KD Gantt library.
 **
 ** This file may be distributed and/or modified under the terms of the
 ** GNU General Public License version 2 as published by the Free Software
 ** Foundation and appearing in the file LICENSE.GPL included in the
 ** packaging of this file.
 **
 ** Licensees holding valid commercial KD Gantt licenses may use this file in
 ** accordance with the KD Gantt Commercial License Agreement provided with
 ** the Software.
 **
 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 **
 ** See http://www.kdab.net/kdgantt for
 **   information about KD Gantt Commercial License Agreements.
 **
 ** Contact info@kdab.net if any conditions of this
 ** licensing are not clear to you.
 **
 **********************************************************************/
#include "kdganttforwardingproxymodel.h"

#include <cassert>

using namespace KDGantt;

typedef QAbstractProxyModel BASE;

/*! Constructor. Creates a new ForwardingProxyModel with
 * parent \a parent
 */
ForwardingProxyModel::ForwardingProxyModel(QObject *parent)
    : BASE(parent)
{
}

ForwardingProxyModel::~ForwardingProxyModel()
{
}

/*! Converts indexes in the source model to indexes in the proxy model */
QModelIndex ForwardingProxyModel::mapFromSource(const QModelIndex &sourceIndex) const
{
    if (!sourceIndex.isValid()) {
        return QModelIndex();
    }
    assert(sourceIndex.model() == sourceModel());

    // Create an index that preserves the internal pointer from the source;
    // this way KDDataConverterProxyModel preserves the structure of the source model
    return createIndex(sourceIndex.row(), sourceIndex.column(), sourceIndex.internalPointer());
}

namespace
{
// Think this is ugly? Well, it's not from me, it comes from QProxyModel
struct KDPrivateModelIndex {
    int r, c;
    void *p;
    const QAbstractItemModel *m;
};
}

/*! Converts indexes in the proxy model to indexes in the source model */
QModelIndex ForwardingProxyModel::mapToSource(const QModelIndex &proxyIndex) const
{
    if (!proxyIndex.isValid()) {
        return QModelIndex();
    }
    assert(proxyIndex.model() == this);
    // So here we need to create a source index which holds that internal pointer.
    // No way to pass it to sourceModel()->index... so we have to do the ugly way:
    QModelIndex sourceIndex;
    KDPrivateModelIndex *hack = reinterpret_cast<KDPrivateModelIndex *>(&sourceIndex);
    hack->r = proxyIndex.row();
    hack->c = proxyIndex.column();
    hack->p = proxyIndex.internalPointer();
    hack->m = sourceModel();
    assert(sourceIndex.isValid());
    return sourceIndex;
}

/*! Sets the model to be used as the source model for this proxy.
 * The proxy does not take ownership of the model.
 * \see QAbstractProxyModel::setSourceModel
 */
void ForwardingProxyModel::setSourceModel(QAbstractItemModel *model)
{
    if (sourceModel()) {
        sourceModel()->disconnect(this);
    }
    BASE::setSourceModel(model);

    if (!model) {
        return;
    }

    connect(model, &QAbstractItemModel::modelAboutToBeReset, this, &ForwardingProxyModel::sourceModelAboutToBeReset);
    connect(model, &QAbstractItemModel::modelReset, this, &ForwardingProxyModel::sourceModelReset);
    connect(model, &QAbstractItemModel::layoutAboutToBeChanged, this, &ForwardingProxyModel::sourceLayoutAboutToBeChanged);
    connect(model, &QAbstractItemModel::layoutChanged, this, &ForwardingProxyModel::sourceLayoutChanged);

    connect(model, &QAbstractItemModel::dataChanged,
            this, &ForwardingProxyModel::sourceDataChanged);

    connect(model,  &QAbstractItemModel::columnsAboutToBeInserted,
            this, &ForwardingProxyModel::sourceColumnsAboutToBeInserted);
    connect(model,  &QAbstractItemModel::columnsInserted,
            this, &ForwardingProxyModel::sourceColumnsInserted);
    connect(model,  &QAbstractItemModel::columnsAboutToBeRemoved,
            this, &ForwardingProxyModel::sourceColumnsAboutToBeRemoved);
    connect(model,  &QAbstractItemModel::columnsRemoved,
            this, &ForwardingProxyModel::sourceColumnsRemoved);

    connect(model,  &QAbstractItemModel::rowsAboutToBeInserted,
            this, &ForwardingProxyModel::sourceRowsAboutToBeInserted);
    connect(model,  &QAbstractItemModel::rowsInserted,
            this, &ForwardingProxyModel::sourceRowsInserted);
    connect(model,  &QAbstractItemModel::rowsAboutToBeRemoved,
            this, &ForwardingProxyModel::sourceRowsAboutToBeRemoved);
    connect(model,  &QAbstractItemModel::rowsRemoved,
            this, &ForwardingProxyModel::sourceRowsRemoved);
}

/*! Called when the source model is about to be reset.
 * \sa QAbstractItemModel::modelAboutToBeReset()
 */
void ForwardingProxyModel::sourceModelAboutToBeReset()
{
    // The matching signal is emitted be reset()
}

/*! Called when the source model is reset
 * \sa QAbstractItemModel::modelReset()
 */
void ForwardingProxyModel::sourceModelReset()
{
    //qCDebug(KDGANTT_LOG) << "ForwardingProxyModel::sourceModelReset()";
    reset();
}

/*! Called just before the layout of the source model is changed.
 * \sa QAbstractItemModel::layoutAboutToBeChanged()
 */

void ForwardingProxyModel::sourceLayoutAboutToBeChanged()
{
    //qCDebug(KDGANTT_LOG) << "ForwardingProxyModel::sourceLayoutAboutToBeChanged()";
    Q_EMIT layoutAboutToBeChanged();
}

/*! Called when the layout of the source model has changed.
 * \sa QAbstractItemModel::layoutChanged()
 */
void ForwardingProxyModel::sourceLayoutChanged()
{
    //qCDebug(KDGANTT_LOG) << "ForwardingProxyModel::sourceLayoutChanged()";
    reset();
}

/*! Called when the data in an existing item in the source model changes.
 * \sa QAbstractItemModel::dataChanged()
 */
void ForwardingProxyModel::sourceDataChanged(const QModelIndex &from, const QModelIndex &to)
{
    //qCDebug(KDGANTT_LOG) << "ForwardingProxyModel::sourceDataChanged("<<from<<to<<")";
    Q_EMIT dataChanged(mapFromSource(from), mapFromSource(to));
}

/*! Called just before columns are inserted into the source model.
 * \sa QAbstractItemModel::columnsAboutToBeInserted()
 */
void ForwardingProxyModel::sourceColumnsAboutToBeInserted(const QModelIndex &parentIdx,
        int start,
        int end)
{
    beginInsertColumns(mapFromSource(parentIdx), start, end);
}

/*! Called after columns have been inserted into the source model.
 * \sa QAbstractItemModel::columnsInserted()
 */
void ForwardingProxyModel::sourceColumnsInserted(const QModelIndex &parentIdx, int start, int end)
{
    Q_UNUSED(parentIdx);
    Q_UNUSED(start);
    Q_UNUSED(end);
    endInsertColumns();
}

/*! Called just before columns are removed from the source model.
 * \sa QAbstractItemModel::columnsAboutToBeRemoved()
 */
void ForwardingProxyModel::sourceColumnsAboutToBeRemoved(const QModelIndex &parentIdx,
        int start,
        int end)
{
    beginRemoveColumns(mapFromSource(parentIdx), start, end);
}

/*! Called after columns have been removed from the source model.
 * \sa QAbstractItemModel::columnsRemoved()
 */
void ForwardingProxyModel::sourceColumnsRemoved(const QModelIndex &parentIdx, int start, int end)
{
    Q_UNUSED(parentIdx);
    Q_UNUSED(start);
    Q_UNUSED(end);
    endRemoveColumns();
}

/*! Called just before rows are inserted into the source model.
 * \sa QAbstractItemModel::rowsAboutToBeInserted()
 */
void ForwardingProxyModel::sourceRowsAboutToBeInserted(const QModelIndex &parentIdx, int start, int end)
{
    beginInsertRows(mapFromSource(parentIdx), start, end);
}

/*! Called after rows have been inserted into the source model.
 * \sa QAbstractItemModel::rowsInserted()
 */
void ForwardingProxyModel::sourceRowsInserted(const QModelIndex &parentIdx, int start, int end)
{
    Q_UNUSED(parentIdx);
    Q_UNUSED(start);
    Q_UNUSED(end);
    endInsertRows();
}

/*! Called just before rows are removed from the source model.
 * \sa QAbstractItemModel::rowsAboutToBeRemoved()
 */
void ForwardingProxyModel::sourceRowsAboutToBeRemoved(const QModelIndex &parentIdx, int start, int end)
{
    beginRemoveRows(mapFromSource(parentIdx), start, end);
}

/*! Called after rows have been removed from the source model.
 * \sa QAbstractItemModel::rowsRemoved()
 */
void ForwardingProxyModel::sourceRowsRemoved(const QModelIndex &parentIdx, int start, int end)
{
    Q_UNUSED(parentIdx);
    Q_UNUSED(start);
    Q_UNUSED(end);
    endRemoveRows();
}

/*! \see QAbstractItemModel::rowCount */
int ForwardingProxyModel::rowCount(const QModelIndex &idx) const
{
    return sourceModel()->rowCount(mapToSource(idx));
}

/*! \see QAbstractItemModel::columnCount */
int ForwardingProxyModel::columnCount(const QModelIndex &idx) const
{
    return sourceModel()->columnCount(mapToSource(idx));
}

/*! \see QAbstractItemModel::index */
QModelIndex ForwardingProxyModel::index(int row, int column, const QModelIndex &parent) const
{
    return mapFromSource(sourceModel()->index(row, column, mapToSource(parent)));
}

/*! \see QAbstractItemModel::parent */
QModelIndex ForwardingProxyModel::parent(const QModelIndex &idx) const
{
    return mapFromSource(sourceModel()->parent(mapToSource(idx)));
}

/*! \see QAbstractItemModel::setData */
bool ForwardingProxyModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    //qCDebug(KDGANTT_LOG) << "ForwardingProxyModel::setData( " << index<<value<< role<<")";
    return sourceModel()->setData(mapToSource(index), value, role);
}

