/*
 * Copyright (c) Contributors to the Open 3D Engine Project.
 * For complete copyright and license terms please see the LICENSE at the root of this distribution.
 *
 * SPDX-License-Identifier: Apache-2.0 OR MIT
 *
 */


#include "DeepFilterProxyModel.h"
#include <QPalette>

DeepFilterProxyModel::DeepFilterProxyModel(QObject* parent)
    : QSortFilterProxyModel(parent)
{
}

void DeepFilterProxyModel::setFilterString(const QString& filter)
{
    m_filter = filter;
    m_filterParts = m_filter.split(' ', Qt::SkipEmptyParts);
    m_acceptCache.clear();
}

void DeepFilterProxyModel::invalidate()
{
    QSortFilterProxyModel::invalidate();
    m_acceptCache.clear();
}

QVariant DeepFilterProxyModel::data(const QModelIndex& index, int role) const
{
    if (role == Qt::ForegroundRole)
    {
        QModelIndex sourceIndex = mapToSource(index);
        if (matchFilter(sourceIndex.row(), sourceIndex.parent()))
        {
            return QSortFilterProxyModel::data(index, role);
        }
        else
        {
            return QPalette().color(QPalette::Disabled, QPalette::Text);
        }
    }
    else
    {
        return QSortFilterProxyModel::data(index, role);
    }
}


void DeepFilterProxyModel::setFilterWildcard(const QString& pattern)
{
    m_acceptCache.clear();
    QSortFilterProxyModel::setFilterWildcard(pattern);
}

bool DeepFilterProxyModel::matchFilter(int sourceRow, const QModelIndex& sourceParent) const
{
    int columnCount = sourceModel()->columnCount(sourceParent);
    for (int i = 0; i < m_filterParts.size(); ++i)
    {
        bool atLeastOneContains = false;
        for (int j = 0; j < columnCount; ++j)
        {
            QModelIndex index = sourceModel()->index(sourceRow, j, sourceParent);
            QVariant data = sourceModel()->data(index, Qt::DisplayRole);
            QString str(data.toString());
            if (str.isEmpty())
            {
                if (m_filterParts.empty())
                {
                    atLeastOneContains = true;
                }
            }
            else if (str.contains(m_filterParts[i], Qt::CaseInsensitive))
            {
                atLeastOneContains = true;
            }
        }
        if (!atLeastOneContains)
        {
            return false;
        }
    }
    return true;
}

bool DeepFilterProxyModel::filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const
{
    if (matchFilter(sourceRow, sourceParent))
    {
        return true;
    }

    if (hasAcceptedChildrenCached(sourceRow, sourceParent))
    {
        return true;
    }

    return false;
}

bool DeepFilterProxyModel::hasAcceptedChildrenCached(int sourceRow, const QModelIndex& sourceParent) const
{
    std::pair<QModelIndex, int> indexId = std::make_pair(sourceParent, sourceRow);
    TAcceptCache::iterator it = m_acceptCache.find(indexId);
    if (it == m_acceptCache.end())
    {
        bool result = hasAcceptedChildren(sourceRow, sourceParent);
        m_acceptCache[indexId] = result;
        return result;
    }
    else
    {
        return it->second;
    }
}

bool DeepFilterProxyModel::hasAcceptedChildren(int sourceRow, const QModelIndex& sourceParent) const
{
    QModelIndex item = sourceModel()->index(sourceRow, 0, sourceParent);
    if (!item.isValid())
    {
        return false;
    }

    int childCount = item.model()->rowCount(item);
    if (childCount == 0)
    {
        return false;
    }

    for (int i = 0; i < childCount; ++i)
    {
        if (filterAcceptsRow(i, item))
        {
            return true;
        }
    }

    return false;
}

QModelIndex DeepFilterProxyModel::findFirstMatchingIndex(const QModelIndex& root)
{
    int rowCount = this->rowCount(root);
    for (int i = 0; i < rowCount; ++i)
    {
        QModelIndex index = this->index(i, 0, root);
        if (!index.isValid())
        {
            continue;
        }
        QModelIndex sourceIndex = mapToSource(index);
        if (!sourceIndex.isValid())
        {
            continue;
        }
        if (matchFilter(sourceIndex.row(), sourceIndex.parent()))
        {
            return index;
        }

        QModelIndex child = findFirstMatchingIndex(index);
        if (child.isValid())
        {
            return child;
        }
    }
    return QModelIndex();
}
