/*******************************************************************************
**
** SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru>
** SPDX-License-Identifier: BSD-3-Clause
** This file is part of the OfficeViewer project.
**
** Redistribution and use in source and binary forms,
** with or without modification, are permitted provided
** that the following conditions are met:
**
** * Redistributions of source code must retain the above copyright notice,
**   this list of conditions and the following disclaimer.
** * 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.
** * 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.
**
*******************************************************************************/



#include "documentlistmodel.h"

#include <QDir>

struct DocumentListModelEntry
{
    QString fileName;
    QString filePath;
    QString fileType;
    int fileSize;
    QDateTime fileRead;
    QString mimeType;
    DocumentListModel::DocumentClass documentClass;
    bool dirty; // When true, should be removed from list.
};

class DocumentListModel::Private
{
public:
    Private()
    {
        roles.insert(FileNameRole, "fileName");
        roles.insert(FilePathRole, "filePath");
        roles.insert(FileTypeRole, "fileType");
        roles.insert(FileSizeRole, "fileSize");
        roles.insert(FileReadRole, "fileRead");
        roles.insert(FileMimeTypeRole, "fileMimeType");
        roles.insert(FileDocumentClass, "fileDocumentClass");
        roles.insert(FileTypeAndNameRole, "fileTypeAndNameRole");
    }
    QList<DocumentListModelEntry> entries;
    QHash<int, QByteArray> roles;
};

DocumentListModel::DocumentListModel(QObject *parent)
    : QAbstractListModel(parent)
    , d(new Private)
{}

DocumentListModel::~DocumentListModel() {}

QVariant DocumentListModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid() || index.row() < 0 || index.row() >= d->entries.count())
        return QVariant();

    switch (role) {
    case FileNameRole:
        return d->entries.at(index.row()).fileName;
    case FilePathRole:
        return d->entries.at(index.row()).filePath;
    case FileTypeRole:
        return d->entries.at(index.row()).fileType;
    case FileSizeRole:
        return d->entries.at(index.row()).fileSize;
    case FileReadRole:
        return d->entries.at(index.row()).fileRead;
    case FileMimeTypeRole:
        return d->entries.at(index.row()).mimeType;
    case FileDocumentClass:
        return d->entries.at(index.row()).documentClass;
    case FileTypeAndNameRole:
        return QString("%1_%2").arg(d->entries.at(index.row()).fileType, d->entries.at(index.row()).fileName);
    default:
        break;
    }

    return QVariant();
}

int DocumentListModel::rowCount(const QModelIndex &parent) const
{
    if (parent.isValid())
        return 0;
    return d->entries.count();
}

QHash<int, QByteArray> DocumentListModel::roleNames() const
{
    return d->roles;
}

void DocumentListModel::setAllItemsDirty(bool status)
{
    for (auto entry = d->entries.begin(); entry != d->entries.end(); ++entry) {
        entry->dirty = status;
    }
}

void DocumentListModel::addItem(QString name, QString path, QString type, int size, QDateTime lastRead, QString mimeType)
{
    // We sometimes get duplicate entries... and that's kind of silly.
    int row = 0;
    for (auto entry = d->entries.begin(); entry != d->entries.end(); ++entry, ++row) {
        if (entry->filePath == path) {
            entry->dirty = false;
            entry->fileType = type;
            entry->fileSize = size;
            entry->fileRead = lastRead;
            entry->mimeType = mimeType;
            entry->documentClass = static_cast<DocumentClass>(mimeTypeToDocumentClass(mimeType));
            emit dataChanged(index(row), index(row));
            return;
        }
    }

    DocumentListModelEntry entry;
    entry.dirty = false;
    entry.fileName = name;
    entry.filePath = path;
    entry.fileType = type;
    entry.fileSize = size;
    entry.fileRead = lastRead;
    entry.mimeType = mimeType;
    entry.documentClass = static_cast<DocumentClass>(mimeTypeToDocumentClass(mimeType));

    int index = 0;
    for (; index < d->entries.count(); ++index) {
        if (d->entries.at(index).fileRead < entry.fileRead)
            break;
    }

    beginInsertRows(QModelIndex(), index, index);
    d->entries.insert(index, entry);
    endInsertRows();
    emit rowCountChanged();
}

void DocumentListModel::removeItemsDirty()
{
    for (int index = 0; index < d->entries.count();) {
        if (d->entries.at(index).dirty) {
            beginRemoveRows(QModelIndex(), index, index);
            emit fileDeleted(d->entries.at(index).filePath);
            d->entries.removeAt(index);
            endRemoveRows();
            emit rowCountChanged();
        } else {
            ++index;
        }
    }
}

void DocumentListModel::removeAt(int index)
{
    if (index > -1 && index < d->entries.count()) {
        beginRemoveRows(QModelIndex(), index, index);
        d->entries.removeAt(index);
        endRemoveRows();
        emit rowCountChanged();
    }
}

void DocumentListModel::clear()
{
    beginResetModel();
    d->entries.clear();
    endResetModel();
    emit rowCountChanged();
}

int DocumentListModel::mimeTypeToDocumentClass(QString mimeType) const
{
    DocumentClass documentClass = UnknownDocument;
    if (mimeType == QLatin1String("application/vnd.oasis.opendocument.text") || mimeType == QLatin1String("application/msword")
        || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.wordprocessing")
        || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.wordprocessingml.document")
        || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.wordprocessingml.template")
        || mimeType == QLatin1String("application/vnd.ms-word.document.macroEnabled.12")
        || mimeType == QLatin1String("application/vnd.ms-word.template.macroEnabled.12") || mimeType == QLatin1String("application/rtf")) {
        documentClass = TextDocument;
    } else if (mimeType == QLatin1String("text/plain")) {
        documentClass = PlainTextDocument;
    } else if (mimeType == QLatin1String("application/vnd.oasis.opendocument.presentation")
               || mimeType == QLatin1String("application/vnd.ms-powerpoint")
               || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.presentationml.presentation")
               || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.presentationml.template")
               || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.presentationml.slideshow")
               || mimeType == QLatin1String("application/vnd.ms-powerpoint.presentation.macroEnabled.12")
               || mimeType == QLatin1String("application/vnd.ms-powerpoint.template.macroEnabled.12")
               || mimeType == QLatin1String("application/vnd.ms-powerpoint.slideshow.macroEnabled.12")) {
        documentClass = PresentationDocument;
    } else if (mimeType == QLatin1String("application/vnd.oasis.opendocument.spreadsheet") || mimeType == QLatin1String("application/vnd.ms-excel")
               || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
               || mimeType == QLatin1String("application/vnd.openxmlformats-officedocument.spreadsheetml.template")
               || mimeType == QLatin1String("application/vnd.ms-excel.sheet.macroEnabled")
               || mimeType == QLatin1String("application/vnd.ms-excel.sheet.macroEnabled.12")
               || mimeType == QLatin1String("application/vnd.ms-excel.template.macroEnabled.12") || mimeType == QLatin1String("text/csv")) {
        documentClass = SpreadSheetDocument;
    } else if (mimeType == QLatin1String("application/pdf")) {
        documentClass = PDFDocument;
    }

    return documentClass;
}

QVariantMap DocumentListModel::getFileInfo(const QString &filePath)
{
    const auto fileInfo = QFileInfo{filePath};
    return QVariantMap{
        { "filePath", fileInfo.filePath() },
        { "fileName", fileInfo.fileName() },
        { "baseName", fileInfo.baseName() },
        { "suffix", fileInfo.suffix() }
    };
}
