/*****************************************************************************
 * $CAMITK_LICENCE_BEGIN$
 *
 * CamiTK - Computer Assisted Medical Intervention ToolKit
 * (c) 2001-2025 Univ. Grenoble Alpes, CNRS, Grenoble INP - UGA, TIMC, 38000 Grenoble, France
 *
 * Visit http://camitk.imag.fr for more information
 *
 * This file is part of CamiTK.
 *
 * CamiTK is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * CamiTK is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with CamiTK.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $CAMITK_LICENCE_END$
 ****************************************************************************/

#include "MeshComponent.h"
#include "MeshDataModel.h"

#include <vtkCellData.h>
#include <vtkPointData.h>
#include <vtkDoubleArray.h>


namespace camitk {

// -------------------- constructor --------------------
MeshDataModel::MeshDataModel(MeshComponent* meshComp) :
    QAbstractTableModel(meshComp), meshComponent(meshComp) {
    // get ready to reset table
    beginResetModel();
}

// -------------------- rowCount --------------------
int MeshDataModel::rowCount(const QModelIndex& parent) const {
    if (meshComponent == nullptr) {
        return 0;
    }
    else {
        return meshComponent->getNumberOfDataArray();
    }
}

// -------------------- columnCount --------------------
int MeshDataModel::columnCount(const QModelIndex& parent) const {
    return 3;
}

// -------------------- getRowInfo --------------------
void MeshDataModel::getRowInfo(const int row, int* dataIndex, MeshComponent::FieldType* field, MeshComponent::DataType* type, QString& name) const {
    // as getNumberOfDataArray(..) will only return the "normal" data (not the specific 3D vectors representation)
    // this should be OK.
    // caveat: if the user has added specific 3D vector representation and then add another point data, then
    // the count will be increased of one, and the first specific 3D vector representation will show.
    int nbPointData = meshComponent->getNumberOfDataArray(MeshComponent::POINTS);
    int nbCellData = meshComponent->getNumberOfDataArray(MeshComponent::CELLS);

    if (row < nbPointData) {
        *dataIndex = row;
        *field = MeshComponent::POINTS;
    }
    else if (row < (nbPointData + nbCellData)) {
        *dataIndex = row - nbPointData;
        *field = MeshComponent::CELLS;
    }
    else {
        *dataIndex = row - (nbPointData + nbCellData);
        *field = MeshComponent::MESH;
    }
    if (*field != MeshComponent::MESH) {
        vtkSmartPointer<vtkDataArray> dataArray = meshComponent->getDataArray(*field, *dataIndex);
        name = dataArray->GetName();
        *type = MeshComponent::getDataType(dataArray);
    }
    else {
        *type = MeshComponent::OTHERS;
    }
}

// -------------------- data --------------------
QVariant MeshDataModel::data(const QModelIndex& index, int role) const {
    if (!meshComponent || !(meshComponent->getPointSet())) {
        return QVariant();
    }

    const int row = index.row();
    int column = index.column();

    int dataIndex;
    MeshComponent::FieldType fieldType;
    MeshComponent::DataType type;
    QString arrayName;
    getRowInfo(row, &dataIndex, &fieldType, &type, arrayName);

    switch (role) {
        case Qt::DisplayRole:
            switch (column) {
                case 0 :
                    return arrayName;
                    break;

                case 1 :
                    return MeshComponent::getFieldName(fieldType);
                    break;

                case 2 :
                    return MeshComponent::getDataTypeName(type);
                    break;

                default:
                    return QVariant();
                    break;
            }

            break;

        case Qt::CheckStateRole:

            if ((column == 0) && fieldType != MeshComponent::MESH) {
                if (meshComponent->getDataRepresentationVisibility(fieldType, arrayName)) {
                    return Qt::Checked;
                }
                else {
                    return Qt::Unchecked;
                }
            }
            else {
                return QVariant();
            }

        case Qt::DecorationRole :

            switch (column) {
                case 1:
                    switch (fieldType) {
                        case MeshComponent::POINTS:
                            return QIcon(":/points");
                            break;
                        case MeshComponent::CELLS:
                            return QIcon(":/cell");
                            break;
                        default:
                            return QVariant();
                    }
                    break;
                case 2:
                    switch (type) {
                        case MeshComponent::SCALARS:
                            return QIcon(":/scalars");
                            break;
                        case MeshComponent::VECTORS:
                            return QIcon(":/vectors");
                            break;
                        default:
                            return QVariant();
                    }
                    break;
                default:
                    return QVariant();
            }

            break;

        case Qt::BackgroundRole :
            break;

        default:
            return QVariant();
            break;
    }

    return QVariant();
}

// -------------------- setData --------------------
bool MeshDataModel::setData(const QModelIndex& index, const QVariant& value, int role) {

    // the only part that can be modified by the user is the check box of column #0
    if (role == Qt::CheckStateRole && index.column() == 0) {
        int row = index.row();
        int dataIndex;
        MeshComponent::FieldType fieldType;
        MeshComponent::DataType type;
        QString arrayName;
        getRowInfo(row, &dataIndex, &fieldType, &type, arrayName);

        if (fieldType == MeshComponent::MESH) {
            // nothing to do if this is a MESH data
            return false;
        }
        else {
            meshComponent->setDataRepresentationVisibility(fieldType, arrayName, (value == Qt::Checked));

            return true;
        }

    }
    else {
        return false;
    }
}

// -------------------- flags --------------------
Qt::ItemFlags MeshDataModel::flags(const QModelIndex& index) const {
    if (index.column() == 0) {
        return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsUserCheckable;
    }
    else {
        return Qt::ItemIsSelectable  | Qt::ItemIsEnabled ;
    }
}


// -------------------- headerData --------------------
QVariant MeshDataModel::headerData(int section, Qt::Orientation orientation, int role) const {
    if (role == Qt::DisplayRole) {
        if (orientation == Qt::Horizontal) {
            switch (section) {
                case 0:
                    return QString("Name");
                    break;

                case 1:
                    return QString("Field");
                    break;

                case 2:
                    return QString("Type");
                    break;

                default:
                    return QVariant();
                    break;
            }
        }
    }

    return QVariant();
}

// -------------------- refresh --------------------
void MeshDataModel::refresh() {
    endResetModel();
}




// ------------------- MeshDataFilterModel ------------------------



// -------------------- MeshDataFilterModel constructor --------------------
MeshDataFilterModel::MeshDataFilterModel(int fieldFilter, int dataFilter, QObject* parent) :
    QSortFilterProxyModel(parent),
    fieldTypeFilter(fieldFilter),
    dataTypeFilter(dataFilter) {
}

// -------------------- setFieldTypeFilter --------------------
void MeshDataFilterModel::setFieldTypeFilter(int fieldFilter) {
    fieldTypeFilter = fieldFilter;
}

// -------------------- setDataTypeFilter --------------------
void MeshDataFilterModel::setDataTypeFilter(int dataFilter) {
    dataTypeFilter = dataFilter;
}

// -------------------- filterAcceptsRow --------------------
bool MeshDataFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const {
    QModelIndex fieldIndex = sourceModel()->index(sourceRow, 1, sourceParent);
    QModelIndex typeIndex = sourceModel()->index(sourceRow, 2, sourceParent);

    return (MeshComponent::getFieldNames().key(sourceModel()->data(fieldIndex).toString(), 0) & fieldTypeFilter) &&
           (MeshComponent::getDataTypeNames().key(sourceModel()->data(typeIndex).toString(), 0) & dataTypeFilter);
}

}
