/* This file is part of the KDE project
 * SPDX-FileCopyrightText: 2011 Gopalakrishna Bhat A <gopalakbhat@gmail.com>
 *
 * SPDX-License-Identifier: LGPL-2.0-or-later
 */

#include "TableOfContentsStyleModel.h"

#include "KoParagraphStyle.h"
#include "KoStyleManager.h"
#include "KoTableOfContentsGeneratorInfo.h"
#include "ToCBibGeneratorInfo.h"
#include <KoStyleThumbnailer.h>

#include <QPair>

#include <KLocalizedString>

TableOfContentsStyleModel::TableOfContentsStyleModel(const KoStyleManager *manager, KoTableOfContentsGeneratorInfo *info)
    : QAbstractTableModel()
    , m_styleManager(manager)
    , m_styleThumbnailer(new KoStyleThumbnailer())
    , m_tocInfo(info)
{
    Q_ASSERT(manager);
    Q_ASSERT(info);

    m_styleThumbnailer->setThumbnailSize(QSize(250, 48));

    foreach (const KoParagraphStyle *style, m_styleManager->paragraphStyles()) {
        m_styleList.append(style->styleId());
        m_outlineLevel.append(getOutlineLevel(style->styleId()));
    }
}

QModelIndex TableOfContentsStyleModel::index(int row, int column, const QModelIndex &parent) const
{
    if (row < 0 || column < 0 || column > 1)
        return QModelIndex();

    if (!parent.isValid()) {
        if (row >= m_styleList.count())
            return QModelIndex();

        QPair<int, int> *modelValue = new QPair<int, int>(m_styleList[row], m_outlineLevel[row]);
        return createIndex(row, column, modelValue);
    }
    return QModelIndex();
}

int TableOfContentsStyleModel::rowCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return m_styleList.count();
    return 0;
}

int TableOfContentsStyleModel::columnCount(const QModelIndex &parent) const
{
    if (!parent.isValid())
        return 2;
    return 0;
}

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

    int id = static_cast<QPair<int, int> *>(index.internalPointer())->first;
    if (index.column() == 0) {
        switch (role) {
        case Qt::DisplayRole: {
            return QVariant();
        }
        case Qt::DecorationRole: {
            if (!m_styleThumbnailer) {
                return QPixmap();
            }
            KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(id);
            if (paragStyle) {
                return m_styleThumbnailer->thumbnail(paragStyle);
            }
            break;
        }
        default:
            break;
        }
    } else {
        KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(id);
        switch (role) {
        case Qt::DisplayRole:
            if (paragStyle) {
                if (QVariant(static_cast<QPair<int, int> *>(index.internalPointer())->second).value<int>() == 0)
                    return QVariant(i18n("Disabled"));
                else
                    return QVariant(static_cast<QPair<int, int> *>(index.internalPointer())->second);
            }
            // fall through
        case Qt::EditRole:
            if (paragStyle)
                return QVariant(static_cast<QPair<int, int> *>(index.internalPointer())->second);
            // fall through
        default:
            break;
        }
    }
    return QVariant();
}

Qt::ItemFlags TableOfContentsStyleModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return {};
    if (index.column() == 0) {
        return (Qt::ItemIsSelectable | Qt::ItemIsEnabled);
    }
    if (index.column() == 1) {
        return (Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable);
    }
    return {};
}

bool TableOfContentsStyleModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    static_cast<QPair<int, int> *>(index.internalPointer())->second = value.toInt();
    QAbstractTableModel::setData(index, value, role);
    m_outlineLevel[index.row()] = value.toInt();
    return true;
}

void TableOfContentsStyleModel::saveData()
{
    int row = 0;

    foreach (const int styleId, m_styleList) {
        KoParagraphStyle *paragStyle = m_styleManager->paragraphStyle(styleId);
        if (paragStyle) {
            setOutlineLevel(styleId, m_outlineLevel[row]);
        }
        row++;
    }
}

int TableOfContentsStyleModel::getOutlineLevel(int styleId)
{
    foreach (const IndexSourceStyles &indexSourceStyles, m_tocInfo->m_indexSourceStyles) {
        foreach (const IndexSourceStyle &indexStyle, indexSourceStyles.styles) {
            if (m_styleManager->paragraphStyle(indexStyle.styleId) && styleId == indexStyle.styleId) {
                return indexSourceStyles.outlineLevel;
            }
        }
    }
    return 0;
}

void TableOfContentsStyleModel::setOutlineLevel(int styleId, int outLineLevel)
{
    // ignore changes to paragraph styles with KoParagraphStyle::OutlineLevel property set.
    // i.e. those considered by KoTableOfContentsGeneratorInfo::m_useOutlineLevel==true
    if (m_styleManager->paragraphStyle(styleId)->hasProperty(KoParagraphStyle::OutlineLevel)) {
        return;
    }

    // check if the outlineLevel has changed
    if (getOutlineLevel(styleId) == outLineLevel) {
        return;
    }

    // now insert the style at the correct place( remove from the old place first and then insert at the new level)
    IndexSourceStyle indexStyleMoved;
    bool styleFound = false;
    int sourceStyleIndex = 0;
    foreach (const IndexSourceStyles &indexSourceStyles, m_tocInfo->m_indexSourceStyles) {
        int index = 0;
        foreach (const IndexSourceStyle &indexStyle, indexSourceStyles.styles) {
            if (styleId == indexStyle.styleId) {
                styleFound = true;
                indexStyleMoved = m_tocInfo->m_indexSourceStyles[sourceStyleIndex].styles.takeAt(index);
                break;
            }
            index++;

            if (styleFound == true) {
                break;
            }
        }
        sourceStyleIndex++;
    }

    // this style is not in the IndexSourceStyles list so fill it
    if (!styleFound) {
        indexStyleMoved.styleId = styleId;
        indexStyleMoved.styleName = m_styleManager->paragraphStyle(styleId)->name();
    }

    // check if IndexSourceStyles are there for this outlineLevel, if not create it
    bool sourceStylePresent = false;
    foreach (const IndexSourceStyles &indexSourceStyles, m_tocInfo->m_indexSourceStyles) {
        if (outLineLevel == indexSourceStyles.outlineLevel) {
            sourceStylePresent = true;
            break;
        }
    }

    if (!sourceStylePresent) {
        IndexSourceStyles indexStyles;
        indexStyles.outlineLevel = outLineLevel;
        m_tocInfo->m_indexSourceStyles.append(indexStyles);
    }

    sourceStyleIndex = 0;
    foreach (const IndexSourceStyles &indexSourceStyles, m_tocInfo->m_indexSourceStyles) {
        if (outLineLevel == indexSourceStyles.outlineLevel) {
            m_tocInfo->m_indexSourceStyles[sourceStyleIndex].styles.append(indexStyleMoved);
            break;
        }
        sourceStyleIndex++;
    }
}

QVariant TableOfContentsStyleModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
        if (section == 0) {
            return i18n("Styles");
        } else if (section == 1) {
            return i18n("Level");
        } else {
            return QAbstractTableModel::headerData(section, orientation, role);
        }
    } else {
        return QAbstractTableModel::headerData(section, orientation, role);
    }
}
