// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2008 Torsten Rahn <tackat@kde.org>
// SPDX-FileCopyrightText: 2009 Jens-Michael Hoffmann <jensmh@gmx.de>
// SPDX-FileCopyrightText: 2011, 2012 Bernahrd Beschow <bbeschow@cs.tu-berlin.de>
//

// Own
#include "LayerManager.h"

// Local dir
#include "AbstractDataPlugin.h"
#include "AbstractDataPluginItem.h"
#include "GeoPainter.h"
#include "LayerInterface.h"
#include "MarbleDebug.h"
#include "RenderPlugin.h"
#include "RenderState.h"

#include <QElapsedTimer>

namespace Marble
{

class Q_DECL_HIDDEN LayerManager::Private
{
public:
    Private(LayerManager *parent);
    ~Private();

    void updateVisibility(bool visible, const QString &nameId);

    LayerManager *const q;

    QList<RenderPlugin *> m_renderPlugins;
    QList<AbstractDataPlugin *> m_dataPlugins;
    QList<LayerInterface *> m_internalLayers;

    RenderState m_renderState;

    bool m_showBackground;
    bool m_showRuntimeTrace;
};

LayerManager::Private::Private(LayerManager *parent)
    : q(parent)
    , m_renderPlugins()
    , m_showBackground(true)
    , m_showRuntimeTrace(false)
{
}

LayerManager::Private::~Private() = default;

void LayerManager::Private::updateVisibility(bool visible, const QString &nameId)
{
    Q_EMIT q->visibilityChanged(nameId, visible);
}

LayerManager::LayerManager(QObject *parent)
    : QObject(parent)
    , d(new Private(this))
{
}

LayerManager::~LayerManager()
{
    delete d;
}

bool LayerManager::showBackground() const
{
    return d->m_showBackground;
}

bool LayerManager::showRuntimeTrace() const
{
    return d->m_showRuntimeTrace;
}

void LayerManager::addRenderPlugin(RenderPlugin *renderPlugin)
{
    d->m_renderPlugins.append(renderPlugin);

    connect(renderPlugin, SIGNAL(settingsChanged(QString)), this, SIGNAL(pluginSettingsChanged()));
    connect(renderPlugin, SIGNAL(repaintNeeded(QRegion)), this, SIGNAL(repaintNeeded(QRegion)));
    connect(renderPlugin, SIGNAL(visibilityChanged(bool, QString)), this, SLOT(updateVisibility(bool, QString)));

    // get data plugins
    auto const dataPlugin = qobject_cast<AbstractDataPlugin *>(renderPlugin);
    if (dataPlugin) {
        d->m_dataPlugins.append(dataPlugin);
    }
}

QList<AbstractDataPlugin *> LayerManager::dataPlugins() const
{
    return d->m_dataPlugins;
}

QList<AbstractDataPluginItem *> LayerManager::whichItemAt(const QPoint &curpos) const
{
    QList<AbstractDataPluginItem *> itemList;

    for (auto plugin : d->m_dataPlugins) {
        itemList.append(plugin->whichItemAt(curpos));
    }
    return itemList;
}

void LayerManager::renderLayers(GeoPainter *painter, ViewportParams *viewport)
{
    d->m_renderState = RenderState(QStringLiteral("Marble"));
    QElapsedTimer totalTime;
    totalTime.start();

    QStringList renderPositions;

    if (d->m_showBackground) {
        renderPositions << QStringLiteral("STARS") << QStringLiteral("BEHIND_TARGET");
    }

    renderPositions << QStringLiteral("SURFACE") << QStringLiteral("HOVERS_ABOVE_SURFACE") << QStringLiteral("GRATICULE") << QStringLiteral("PLACEMARKS")
                    << QStringLiteral("ATMOSPHERE") << QStringLiteral("ORBIT") << QStringLiteral("ALWAYS_ON_TOP") << QStringLiteral("FLOAT_ITEM")
                    << QStringLiteral("USER_TOOLS");

    QStringList traceList;
    for (const auto &renderPosition : renderPositions) {
        QList<LayerInterface *> layers;

        // collect all RenderPlugins of current renderPosition
        for (auto renderPlugin : d->m_renderPlugins) {
            if (renderPlugin && renderPlugin->renderPosition().contains(renderPosition)) {
                if (renderPlugin->enabled() && renderPlugin->visible()) {
                    if (!renderPlugin->isInitialized()) {
                        renderPlugin->initialize();
                        Q_EMIT renderPluginInitialized(renderPlugin);
                    }
                    layers.push_back(renderPlugin);
                }
            }
        }

        // collect all internal LayerInterfaces of current renderPosition
        for (auto layer : d->m_internalLayers) {
            if (layer && layer->renderPosition().contains(renderPosition)) {
                layers.push_back(layer);
            }
        }

        // sort them according to their zValue()s
        std::sort(layers.begin(), layers.end(), [](const LayerInterface *const one, const LayerInterface *const two) -> bool {
            Q_ASSERT(one && two);
            return one->zValue() < two->zValue();
        });

        // render the layers of the current renderPosition
        QElapsedTimer timer;
        for (auto layer : layers) {
            timer.start();
            layer->render(painter, viewport, renderPosition, nullptr);
            d->m_renderState.addChild(layer->renderState());
            traceList.append(QStringLiteral("%2 ms %3").arg(timer.elapsed(), 3).arg(layer->runtimeTrace()));
        }
    }

    if (d->m_showRuntimeTrace) {
        const int totalElapsed = totalTime.elapsed();
        const int fps = 1000.0 / totalElapsed;
        traceList.append(QStringLiteral("Total: %1 ms (%2 fps)").arg(totalElapsed, 3).arg(fps));

        painter->save();
        painter->setBackgroundMode(Qt::OpaqueMode);
        painter->setBackground(Qt::gray);
        painter->setFont(QFont(QStringLiteral("Sans Serif"), 10, QFont::Bold));

        int i = 0;
        int const top = 150;
        int const lineHeight = painter->fontMetrics().height();
        for (const auto &text : traceList) {
            painter->setPen(Qt::black);
            painter->drawText(QPoint(10, top + 1 + lineHeight * i), text);
            painter->setPen(Qt::white);
            painter->drawText(QPoint(9, top + lineHeight * i), text);
            ++i;
        }
        painter->restore();
    }
}

void LayerManager::setShowBackground(bool show)
{
    d->m_showBackground = show;
}

void LayerManager::setShowRuntimeTrace(bool show)
{
    d->m_showRuntimeTrace = show;
}

void LayerManager::addLayer(LayerInterface *layer)
{
    if (!d->m_internalLayers.contains(layer)) {
        d->m_internalLayers.push_back(layer);
    }
}

void LayerManager::removeLayer(LayerInterface *layer)
{
    d->m_internalLayers.removeAll(layer);
}

QList<LayerInterface *> LayerManager::internalLayers() const
{
    return d->m_internalLayers;
}

RenderState LayerManager::renderState() const
{
    return d->m_renderState;
}

}

#include "moc_LayerManager.cpp"
