/*******************************************************************************
**
** 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 <QtAlgorithms>
#include <QHash>

#include <debug.h>

#include "lopageprovider.h"
#include "lodocumentprovider.h"

uint qHash(const LoPageProvider::TaskKey &task)
{
    return qHashBits(&task, sizeof(task.pageNumber) + sizeof (task.renderRect)) + qRound(task.zoom * 100);
}

QFuture<QSizeF> LoPageProvider::originalSize() const
{
    if (m_originalSize.isFinished() || m_originalSize.isRunning())
        return m_originalSize.future();

    m_originalSize.reportStarted();
    QSizeF size = QSizeF(m_loProvider->originalSize(m_pageIndex + 1));
    m_originalSize.reportFinished(&size);

    return m_originalSize.future();
}

QFuture<QImage> LoPageProvider::bitmapFull(qreal pageScale, int renderFlags) const
{
    Q_UNUSED(pageScale)
    Q_UNUSED(renderFlags)
    return {};
}

QFuture<QImage> LoPageProvider::bitmapPart(qreal pageScaleX, qreal pageScaleY, int renderFlags, qreal zoom, const QPointF &bias, qreal pageScale) const
{
    Q_UNUSED(renderFlags)
    if (!m_loProvider)
        return {};

    if (!m_originalSize.isFinished())
        return {};

    QFutureInterface<QImage> interface;
    const auto &future = interface.future();

    int width = qRound(originalSize().result().width() * pageScaleX);
    int height = qRound(originalSize().result().height() * pageScaleY);

    QRect tileRect = QRect(bias.x(), bias.y(), width, height);
    auto key = TaskKey(m_pageIndex + 1, tileRect, zoom);

    m_mutex.lock();
    m_taskToItems.insert(key, interface);
    m_mutex.unlock();
    m_loProvider->renderToImage(m_pageIndex + 1, tileRect, tileRect.size() * pageScale, QSize(), zoom);

    interface.reportStarted();

    return future;
}

QFuture<QList<QObject *> > LoPageProvider::words()
{
    return {};
}

void LoPageProvider::stopBitmapPart(qreal pageScaleX, qreal pageScaleY, qreal zoom, const QPointF &bias)
{
    int width = qRound(originalSize().result().width() * pageScaleX);
    int height = qRound(originalSize().result().height() * pageScaleY);
    QRect tileRect = QRect(bias.x(), bias.y(), width, height);
    qCDebug(lcDocviewerRender) << "StopBitmapPart" << tileRect << "page index:" << pageIndex() << "zoom:" << zoom;

    m_mutex.lock();
    if (m_taskToItems.contains({m_pageIndex + 1, tileRect, zoom})) {
        m_loProvider->stopRenderImage(m_pageIndex + 1, tileRect, zoom);
        m_taskToItems.remove({m_pageIndex + 1, tileRect, zoom});
    } else {
        qCDebug(lcDocviewerRender) << "Not stop render tile rect" << tileRect << "page index:" << pageIndex() << "zoom:" << zoom;
    }
    m_mutex.unlock();
}

void LoPageProvider::stopAllBitmapPart()
{
    m_mutex.lock();
    auto keys = m_taskToItems.keys();
    m_mutex.unlock();

    if (keys.count())
        qCDebug(lcDocviewerRender) << "StopAllBitmapPart" << "page index:" << pageIndex() << "tile count:" << keys.count();

    for (auto key : keys) {
        m_loProvider->stopRenderImage(key.pageNumber, key.renderRect, key.zoom);
    }
}

void LoPageProvider::onTileRendered(int pageNumber, const QImage &image, const QRect &documentRect, qreal zoom)
{
     if (pageNumber == m_pageIndex + 1) {
        auto key = TaskKey(pageNumber, documentRect, zoom);

        m_mutex.lock();
        if (m_taskToItems.contains(key)) {
            m_taskToItems[key].reportFinished(&image);
            m_taskToItems.remove(key);
        } else {
            qCWarning(lcDocviewerRender) << "Task render tile not found" << documentRect << "page index:" << m_pageIndex;
        }
        m_mutex.unlock();
    }
}

LoPageProvider::LoPageProvider(int pageIndex, QSharedPointer<LoDocumentProvider> loProvider)
    : m_pageIndex(pageIndex)
    , m_loProvider(loProvider)
{
    connect(m_loProvider.data(), &BaseDocumentProvider::tileRendered, this, &LoPageProvider::onTileRendered, Qt::DirectConnection);
}

LoPageProvider::~LoPageProvider()
{
    stopAllBitmapPart();
}

int LoPageProvider::pageIndex() const
{
    return m_pageIndex;
}

QDebug operator<<(QDebug debug, const LoPageProvider::TaskKey &taskKey)
{
    debug << "[pageNumber=" << taskKey.pageNumber << " zoom=" << taskKey.zoom << " rect=" << taskKey.renderRect << "]";
    return debug;
}
