/*******************************************************************************
**
** 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 <QFutureWatcher>
#include <QImage>

#include <baseannotation.h>
#include "lopage.h"
#include "lopageprovider.h"
#include "lodocument.h"

LoPage::LoPage(QSharedPointer<LoPageProvider> loPageProvider, BaseDocument *document)
    : BasePage(nullptr)
    , m_loPageProvider(loPageProvider)
    , m_loDocument(qobject_cast<LoDocument *>(document))
{
    connect(m_loDocument, &BaseDocument::highlightedUpdated, this, [this] (const QList<QRect> &rects, int pageIndex) {
        Q_UNUSED(rects)
        if (pageIndex == m_loPageProvider->pageIndex()) {
            loadAnnotations();
        } else {
            emit annotationsChanged();
        }
    });
    connect(m_loDocument, &BaseDocument::cellSelectedUpdated, this, [this] (const QList<QRect> &rects, int pageIndex) {
        Q_UNUSED(rects)
        if (pageIndex == m_loPageProvider->pageIndex()) {
            loadAnnotations();
        } else {
            emit annotationsChanged();
        }
    });
    connect(m_loDocument, &BaseDocument::textSelectedUpdated, this, [this] (const QList<QRect> &rects, int pageIndex) {
        Q_UNUSED(rects)
        if (pageIndex == m_loPageProvider->pageIndex()) {
            loadAnnotations();
        } else {
            emit annotationsChanged();
        }
    });
    connect(m_loDocument, &BaseDocument::commentsUpdated, this, [this] (int pageIndex) {
        if (pageIndex == m_loPageProvider->pageIndex()) {
            loadAnnotations();
        } else {
            emit annotationsChanged();
        }
    });
    connect(m_loDocument, &BaseDocument::highlightedAllUpdated, this, [this] { loadAnnotations(); });
    connect(m_loDocument, &BaseDocument::invalidateRect, this, [this] (const QRect &rect, int pageIndex) {
        if (pageIndex == m_loPageProvider->pageIndex()) {
            emit invalidateRect(rect);
        }
    });
}

LoPage::~LoPage()
{
    qDeleteAll(m_annotations);
}

QThreadPool *LoPage::threadPool() const
{
    return m_loDocument ? m_loDocument->threadPool() : nullptr;
}

QList<BaseAnnotation *> LoPage::annotations() const
{
    return m_annotations;
}

void LoPage::loadAnnotations()
{
    if (!m_loDocument)
        return;

    if (!m_annotations.isEmpty()) {
        qDeleteAll(m_annotations);
        m_annotations.clear();
    }

    auto highlightedRects = m_loDocument->currentHighlightedSearch(m_loPageProvider->pageIndex());

    for (const auto &rect : highlightedRects) {
        auto highlightedAnnotation = new BaseAnnotation();
        highlightedAnnotation->rect = rect;
        highlightedAnnotation->type = BaseAnnotation::AnnotationType::CurrentSearch;
        m_annotations.append(highlightedAnnotation);
    }

    auto allHighlightedRects = m_loDocument->allHighlightedSearch(m_loPageProvider->pageIndex());

    for (const auto &rect : allHighlightedRects) {
        auto highlightedAnnotation = new BaseAnnotation();
        highlightedAnnotation->rect = rect;
        highlightedAnnotation->type = BaseAnnotation::AnnotationType::AllSearch;
        m_annotations.append(highlightedAnnotation);
    }

    auto cellSelectedPair = m_loDocument->cellSelected(m_loPageProvider->pageIndex());

    for (const auto &pair : cellSelectedPair) {
        auto highlightedAnnotation = new BaseAnnotation();
        highlightedAnnotation->rect = pair.second;
        highlightedAnnotation->type = BaseAnnotation::AnnotationType::CellSelected;
        highlightedAnnotation->content = pair.first;
        m_annotations.append(highlightedAnnotation);
    }

    auto textSelected = m_loDocument->highlightedTextArea(m_loPageProvider->pageIndex());

    for (const auto &rect : textSelected) {
        auto highlightedAnnotation = new BaseAnnotation();
        highlightedAnnotation->rect = rect;
        highlightedAnnotation->type = BaseAnnotation::AnnotationType::TextSelected;
        m_annotations.append(highlightedAnnotation);
    }

    auto commentDatas = m_loDocument->comments(m_loPageProvider->pageIndex());
    for (const auto &pair : commentDatas) {
        auto comment = new BaseAnnotation();
        comment->rect = pair.second;
        comment->type = BaseAnnotation::AnnotationType::Comment;
        comment->author = pair.first.author;
        comment->content = pair.first.text;
        comment->dateTime = pair.first.dateTime;
        m_annotations.append(comment);
    }

    emit annotationsLoaded();
}

bool LoPage::isAnnotationsSupport() const
{
    return true;
}

int LoPage::pageIndex() const
{
    return m_loPageProvider ? m_loPageProvider->pageIndex() : -1;
}

QFuture<QSizeF> LoPage::originalSize()
{
    return m_loPageProvider ? m_loPageProvider->originalSize() : QFuture<QSizeF>();
}

QFuture<QImage> LoPage::bitmapFull(qreal pageScale, int renderFlags) const
{
    return m_loPageProvider ? m_loPageProvider->bitmapFull(pageScale, renderFlags) : QFuture<QImage>();
}

QFuture<QImage> LoPage::bitmapPart(qreal pageScaleX, qreal pageScaleY, int renderFlags, qreal zoom, const QPointF &bias, qreal pageScale) const
{
    return m_loPageProvider ? m_loPageProvider->bitmapPart(pageScaleX, pageScaleY, renderFlags, zoom, bias, pageScale) : QFuture<QImage>();
}

void LoPage::addAnnotation(const QRectF &rect, const QColor &color, const QString &author, const QString &content)
{
    // TODO: Will be used when adding an annotation feature
}

void LoPage::stopBitmapPart(qreal pageScaleX, qreal pageScaleY, qreal zoom, const QPointF &bias)
{
    if (m_loPageProvider)
        m_loPageProvider->stopBitmapPart(pageScaleX, pageScaleY, zoom, bias);
}

void LoPage::stopAllBitmapPart()
{
    if (m_loPageProvider)
        m_loPageProvider->stopAllBitmapPart();
}

BaseDocumentProvider::DocumentType LoPage::documentType() const
{
    if (m_loDocument)
        return m_loDocument->documentType();

    return BaseDocumentProvider::Other;
}
