/*******************************************************************************
**
** 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 <QtMath>

#include "basepage.h"
#include "pagetile.h"

#include "multipagetile.h"

MultiPageTile::MultiPageTile(QQuickItem *parent) : QQuickItem(parent),
    m_renderable(false),
    m_imageScale(1.0),
    m_tileSize(256),
    m_debugIndex(-1)
{
    setFlag(QQuickItem::ItemHasContents, true);
}

MultiPageTile::~MultiPageTile()
{
    for (auto &tile : m_tilesMap.values())
        tile->deleteLater();

    m_tilesMap.clear();
}

QSGNode *MultiPageTile::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
{
    return oldNode;
}

void MultiPageTile::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
{
    QQuickItem::geometryChanged(newGeometry, oldGeometry);
    _tailorise();
}

void MultiPageTile::setPageSource(QSharedPointer<BasePage> pageSource)
{
    if (!pageSource)
        return;

    m_pageSource = pageSource;
    for (auto &tile : m_tilesMap.values())
        tile->setPageSource(pageSource);
}

bool MultiPageTile::renderable() const
{
    return m_renderable;
}

bool MultiPageTile::isBitmap() const
{
    bool bitmap = m_tilesMap.count();

    for (auto &tile : m_tilesMap.values())
        bitmap &= tile->isBitmap();

    return bitmap;
}

void MultiPageTile::setImageScale(qreal imageScale)
{
    if (qFuzzyCompare(imageScale, m_imageScale))
        return;

    m_imageScale = imageScale;
    for (auto &tile : m_tilesMap.values())
        tile->setImageScale(imageScale);

    _tailorise();
}

void MultiPageTile::render(bool force)
{
    for (auto &tile : m_tilesMap.values())
        tile->render(force);
}

void MultiPageTile::setRenderable(bool renderable)
{
    if (m_renderable == renderable)
        return;

    m_renderable = renderable;
    emit renderableChanged(m_renderable);

    for (auto &tile : m_tilesMap.values())
        tile->setRenderable(renderable);

    QMetaObject::invokeMethod(this, "_tailorise");
}

void MultiPageTile::_tailorise()
{
    if (!m_renderable) {
        for (auto &tile : m_tilesMap.values())
            tile->setVisible(false);

        return;
    }

    auto tileCountX = qCeil(width() / m_tileSize / 3);
    auto tileCountY = qCeil(height() / m_tileSize / 3);
    tileCountX = tileCountX > 0 ? tileCountX : 1;
    tileCountY = tileCountY > 0 ? tileCountY : 1;
    tileCountX = qMax(tileCountX, tileCountY);
    tileCountY = tileCountX;

    if (m_tilesMap.size() != tileCountX * tileCountY) {
        for (auto &tile : m_tilesMap.values()) {
            tile->setEnabled(false);
            tile->setVisible(false);
            tile->deleteLater();
        }

        m_tilesMap.clear();
    }

    auto tileIndex = -1;
    auto actualTileWidth = width() / tileCountX;
    auto actualTileHeight = height() / tileCountY;

    for (int tileX = 0; tileX < tileCountX; ++tileX) {
        for (int tileY = 0; tileY < tileCountY; ++tileY) {
            ++tileIndex;

            if (!m_tilesMap.contains(tileIndex)) {
                auto tile = new PageTile(this);
                tile->setRenderable(m_renderable);
                tile->setDebugText(QString("%1%2").arg(m_debugIndex).arg(char('A' + tileIndex)));
                m_tilesMap.insert(tileIndex, tile);
            }

            auto tile = m_tilesMap.value(tileIndex);
            tile->setImageScale(m_imageScale);
            tile->setVisible(true);

            if (m_pageSource)
                tile->setPageSource(m_pageSource);

            QPointF tilePosition(tileX * actualTileWidth, tileY * actualTileHeight);
            tile->setX(tilePosition.x());
            tile->setY(tilePosition.y());

            auto currentWidth = tilePosition.x() + actualTileWidth;
            if (currentWidth <= width())
                tile->setWidth(actualTileWidth);
            else
                tile->setWidth(actualTileWidth - qAbs(std::remainder(width(), actualTileWidth)));

            auto currentHeight = tilePosition.y() + actualTileHeight;
            if (currentHeight <= height())
                tile->setHeight(actualTileHeight);
            else
                tile->setHeight(actualTileHeight - qAbs(std::remainder(height(), actualTileHeight)));
        }
    }
}

int MultiPageTile::debugIndex() const
{
    return m_debugIndex;
}

void MultiPageTile::setDebugIndex(int newDebugIndex)
{
    m_debugIndex = newDebugIndex;
}

void MultiPageTile::setTileSize(quint16 newTileSize)
{
    m_tileSize = newTileSize;
}
