/*
 * Copyright (c) 2022, Technische Universität Kaiserslautern
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors:
 *    Iron Prando da Silva
 */

#include "dependencymodels.h"

DependencyInfosModel::DependencyInfosModel(TraceDB &traceFile, QObject *parent) : QAbstractItemModel(parent)
{
    mDepInfosDepType = traceFile.getDependencyInfos(DependencyInfos::Type::DependencyType);
    mDepInfosTimeDep = traceFile.getDependencyInfos(DependencyInfos::Type::TimeDependency);
    mDepInfosDelPhase = traceFile.getDependencyInfos(DependencyInfos::Type::DelayedPhase);
    mDepInfosDepPhase = traceFile.getDependencyInfos(DependencyInfos::Type::DependencyPhase);

    if (traceFile.checkDependencyTableExists())
    {
        parseInfos();
    }
}

int DependencyInfosModel::Node::getRow() const
{
    if (!parent)
        return 0;

    const auto &siblings = parent->children;
    const auto siblingsIt = std::find_if(siblings.begin(), siblings.end(),
                                         [this](const std::unique_ptr<Node> &node) { return node.get() == this; });

    Q_ASSERT(siblingsIt != siblings.end());

    return std::distance(siblings.begin(), siblingsIt);
}

void DependencyInfosModel::parseInfos()
{

    std::vector<std::pair<QString, DependencyInfos &>> infos = {{"Dependency Granularity", mDepInfosDepType},
                                                                {"Time Dependencies", mDepInfosTimeDep},
                                                                {"Delayed Phases", mDepInfosDelPhase},
                                                                {"Dependency Phases", mDepInfosDepPhase}};

    for (auto pair : infos)
    {
        std::unique_ptr<Node> node = std::unique_ptr<Node>(new Node({pair.first, ""}, rootNode.get()));

        for (auto v : pair.second.getInfos())
        {
            QString value = QString::number(v.value) + " %";
            node->children.push_back(std::move(std::unique_ptr<Node>(new Node({v.id, value}, node.get()))));
        }

        rootNode->children.push_back(std::move(node));
    }
}

int DependencyInfosModel::rowCount(const QModelIndex &parent) const
{
    if (parent.column() > 0)
        return 0;

    const Node *parentNode;

    if (!parent.isValid())
        parentNode = rootNode.get();
    else
        parentNode = static_cast<const Node *>(parent.internalPointer());

    return parentNode->childCount();
}

int DependencyInfosModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent)

    return 2;
}

QVariant DependencyInfosModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    if (role != Qt::DisplayRole && role != Qt::ToolTipRole)
        return QVariant();

    auto *node = static_cast<const Node *>(index.internalPointer());

    if (index.column() == 0)
        return QVariant(node->data.first);
    else
        return QVariant(node->data.second);
}

QVariant DependencyInfosModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();

    if (orientation == Qt::Horizontal)
    {
        switch (section)
        {
        case 0:
            return "Field";
        case 1:
            return "Percentage";
        default:
            break;
        }
    }

    return QVariant();
}

QModelIndex DependencyInfosModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    const Node *parentNode;

    if (!parent.isValid())
        parentNode = rootNode.get();
    else
        parentNode = static_cast<const Node *>(parent.internalPointer());

    const Node *node = parentNode->children[row].get();

    return createIndex(row, column, const_cast<Node *>(node));
}

QModelIndex DependencyInfosModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    const Node *childNode = static_cast<const Node *>(index.internalPointer());
    const Node *parentNode = childNode->parent;

    if (!parentNode)
        return QModelIndex();

    return createIndex(parentNode->getRow(), 0, const_cast<Node *>(parentNode));
}
