// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2013 Dennis Nienhüser <nienhueser@kde.org>
// Loosely based on MarblePhysics.cpp
//

/**
 * Animated jump from a source point to a destination point, recording
 * the result to a video.
 */

#include <marble/GeoDataCoordinates.h>
#include <marble/GeoDataLineString.h>
#include <marble/MarbleWidget.h>
#include <marble/Quaternion.h>
#include <marble/RenderPlugin.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

#include <QApplication>
#include <QTimeLine>
#include <qmath.h>

#include <cstdio>

using namespace Marble;
using namespace cv;

namespace
{
// Some stuff you might want to change

// The map theme in use
QString const mapTheme = "earth/mapquest-open-aerial/mapquest-open-aerial.dgml";

// Enabled plugins. Everything else will be disabled
QStringList const features = QStringList() << "stars"
                                           << "atmosphere";

// Camera starting point: Position and zoom level
GeoDataCoordinates const source(8.40314, 49.01302, 0.0, GeoDataCoordinates::Degree);
double const sourceZoomLevel = 11;

// Camera destination point: Position and zoom level
GeoDataCoordinates const destination(-101.36631, 43.13718, 0.0, GeoDataCoordinates::Degree);
double const destinationZoomLevel = 14;

// Minimum zoom level (in the middle of the animation)
double const jumpZoomLevel = 5.5;

// Length of the video
QTimeLine timeLine(20 * 1000);

// Frames per second
int const fps = 30;

// Target video file name
std::string const videoFile = "marble-animated-zoom.avi";

// Video resolution
Size frameSize(1280, 720);
}

void interpolate(MarbleWidget *widget, qreal value)
{
    GeoDataCoordinates coordinates;
    qreal lon, lat;
    Quaternion::slerp(source.quaternion(), destination.quaternion(), value).getSpherical(lon, lat);
    coordinates.setLongitude(lon);
    coordinates.setLatitude(lat);
    widget->centerOn(coordinates);
    widget->setRadius(exp(jumpZoomLevel) + (value < 0.5 ? exp(sourceZoomLevel * (1.0 - 2 * value)) : exp(destinationZoomLevel * (2 * value - 1.0))));
}

void animatedFlight(MarbleWidget *mapWidget)
{
    mapWidget->resize(frameSize.width, frameSize.height);
    VideoWriter videoWriter(videoFile, cv::VideoWriter::fourcc('D', 'I', 'V', 'X'), fps, frameSize);
    Mat buffer;
    buffer.create(frameSize, CV_8UC3);
    timeLine.setEasingCurve(QEasingCurve::InOutSine);
    int const frameTime = qRound(1000.0 / fps);
    for (int i = 1; i <= timeLine.duration(); i += frameTime) {
        printf("[%i%% done]\r", cvRound((100.0 * i) / timeLine.duration()));
        fflush(stdout);
        interpolate(mapWidget, timeLine.valueForTime(i));
        QImage screenshot = mapWidget->grab().toImage().convertToFormat(QImage::Format_RGB888);
        Mat converter(frameSize, CV_8UC3);
        converter.data = screenshot.bits();
        cvtColor(converter, buffer, COLOR_RGB2BGR);
        videoWriter.write(buffer);
    }
    for (int i = 0; i < fps; ++i) {
        videoWriter.write(buffer); // one second stand-still at end
    }
    printf("Wrote %s\n", videoFile.c_str());
}

int main(int argc, char **argv)
{
    QApplication app(argc, argv);
    MarbleWidget *mapWidget = new MarbleWidget;
    mapWidget->setMapThemeId(mapTheme);
    const auto plugins = mapWidget->renderPlugins();
    for (RenderPlugin *plugin : plugins) {
        if (!features.contains(plugin->nameId())) {
            plugin->setEnabled(false);
        }
    }

    animatedFlight(mapWidget);
    return 0;
}
