// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2012 Bernhard Beschow <bbeschow@cs.tu-berlin.de>
//

#include "GeoDataAccuracy.h"
#include "GeoDataPlacemark.h"
#include "GeoDataTrack.h"
#include "MarbleModel.h"
#include "PluginManager.h"
#include "PositionProviderPlugin.h"
#include "TestUtils.h"

#include <QSignalSpy>

Q_DECLARE_METATYPE(Marble::GeoDataPlacemark *)

namespace Marble
{

class PlacemarkPositionProviderPluginTest : public QObject
{
    Q_OBJECT

public:
    PlacemarkPositionProviderPluginTest();

    static PositionProviderPlugin *createUninitializedPlugin(const PluginManager *pluginManager);
    static PositionProviderPlugin *createInitializedPlugin(const MarbleModel *model);

private Q_SLOTS:
    void initialize_data();
    void initialize();

    void setTrackedPlacemark_afterInitialize_data();
    void setTrackedPlacemark_afterInitialize();

    void setClockDateTime();

private:
    const QDateTime m_minTime;
    const QDateTime m_maxTime;
    GeoDataPlacemark m_placemark1;
    GeoDataPlacemark m_placemark2;
};

PlacemarkPositionProviderPluginTest::PlacemarkPositionProviderPluginTest()
    : m_minTime(QDate(2000, 1, 1), QTime(0, 0))
    , m_maxTime(QDate(2000, 1, 2), QTime(23, 59))
{
    qRegisterMetaType<GeoDataCoordinates>("GeoDataCoordinates");
    qRegisterMetaType<GeoDataAccuracy>("GeoDataAccuracy");
    qRegisterMetaType<PositionProviderStatus>("PositionProviderStatus");

    auto track = new GeoDataTrack;
    track->setInterpolate(true);
    track->addPoint(m_minTime, GeoDataCoordinates());
    track->addPoint(m_maxTime, GeoDataCoordinates());
    m_placemark1.setGeometry(track);

    m_placemark2 = m_placemark1;
}

PositionProviderPlugin *PlacemarkPositionProviderPluginTest::createUninitializedPlugin(const PluginManager *pluginManager)
{
    const auto plugins = pluginManager->positionProviderPlugins();
    for (const PositionProviderPlugin *plugin : plugins) {
        if (plugin->nameId() == QLatin1StringView("Placemark")) {
            PositionProviderPlugin *instance = plugin->newInstance();
            return instance;
        }
    }

    return nullptr;
}

PositionProviderPlugin *PlacemarkPositionProviderPluginTest::createInitializedPlugin(const MarbleModel *model)
{
    PositionProviderPlugin *const plugin = createUninitializedPlugin(model->pluginManager());

    if (plugin == nullptr) {
        return nullptr;
    }

    plugin->initialize();

    return plugin;
}

void PlacemarkPositionProviderPluginTest::initialize_data()
{
    QTest::addColumn<GeoDataPlacemark *>("placemark");

    GeoDataPlacemark *const nullPlacemark = nullptr;

    addRow() << nullPlacemark;
    addRow() << &m_placemark1;
}

void PlacemarkPositionProviderPluginTest::initialize()
{
    QFETCH(GeoDataPlacemark *, placemark);
    const PositionProviderStatus expectedStatus = placemark ? PositionProviderStatusAvailable : PositionProviderStatusUnavailable;
    const int expectedStatusCount = placemark ? 1 : 0;
    const int expectedPositionCount = placemark ? 1 : 0;

    MarbleModel model;
    model.setClockDateTime(m_minTime); // FIXME crashes when this line is removed

    model.setTrackedPlacemark(placemark);
    QVERIFY(model.trackedPlacemark() == placemark);

    PositionProviderPlugin *const plugin = createUninitializedPlugin(model.pluginManager());
    QVERIFY2(plugin != nullptr, "Need a PlacemarkPositionProviderPlugin!");

    QCOMPARE(plugin->status(), PositionProviderStatusUnavailable);

    QSignalSpy statusChangedSpy(plugin, SIGNAL(statusChanged(PositionProviderStatus)));
    QSignalSpy positionChangedSpy(plugin, SIGNAL(positionChanged(GeoDataCoordinates, GeoDataAccuracy)));

    plugin->initialize();

    QCOMPARE(plugin->status(), expectedStatus);
    QCOMPARE(statusChangedSpy.count(), expectedStatusCount);
    QCOMPARE(positionChangedSpy.count(), expectedPositionCount);

    delete plugin;
}

void PlacemarkPositionProviderPluginTest::setTrackedPlacemark_afterInitialize_data()
{
    QTest::addColumn<GeoDataPlacemark *>("initialPlacemark");
    QTest::addColumn<GeoDataPlacemark *>("newPlacemark");
    QTest::addColumn<int>("expectedStatusCount");

    GeoDataPlacemark *const nullPlacemark = nullptr;

    addRow() << nullPlacemark << nullPlacemark << 0;
    addRow() << nullPlacemark << &m_placemark1 << 1;
    addRow() << &m_placemark1 << nullPlacemark << 1;
    addRow() << &m_placemark1 << &m_placemark2 << 2;
}

void PlacemarkPositionProviderPluginTest::setTrackedPlacemark_afterInitialize()
{
    QFETCH(GeoDataPlacemark *, initialPlacemark);
    QFETCH(GeoDataPlacemark *, newPlacemark);
    QFETCH(int, expectedStatusCount);
    const PositionProviderStatus expectedStatus = newPlacemark ? PositionProviderStatusAvailable : PositionProviderStatusUnavailable;
    const int expectedPositionCount = newPlacemark ? 1 : 0;

    MarbleModel model;
    model.setClockDateTime(m_minTime); // FIXME crashes when this line is removed

    model.setTrackedPlacemark(initialPlacemark);
    QVERIFY(model.trackedPlacemark() == initialPlacemark);

    PositionProviderPlugin *const plugin = createInitializedPlugin(&model);
    QVERIFY2(plugin != nullptr, "Need a PlacemarkPositionProviderPlugin!");

    QSignalSpy statusChangedSpy(plugin, SIGNAL(statusChanged(PositionProviderStatus)));
    QSignalSpy positionChangedSpy(plugin, SIGNAL(positionChanged(GeoDataCoordinates, GeoDataAccuracy)));

    model.setTrackedPlacemark(newPlacemark);

    QCOMPARE(plugin->status(), expectedStatus);
    QCOMPARE(statusChangedSpy.count(), expectedStatusCount);
    QCOMPARE(positionChangedSpy.count(), expectedPositionCount);

    delete plugin;
}

void PlacemarkPositionProviderPluginTest::setClockDateTime()
{
    MarbleModel model;

    model.setClockDateTime(m_minTime); // FIXME crashes when this line is removed

    model.setTrackedPlacemark(&m_placemark1);

    PositionProviderPlugin *const plugin = createInitializedPlugin(&model);
    QVERIFY2(plugin != nullptr, "Need a PlacemarkPositionProviderPlugin!");

    QSignalSpy positionChangedSpy(plugin, SIGNAL(positionChanged(GeoDataCoordinates, GeoDataAccuracy)));

    model.setClockDateTime(m_minTime);

    QCOMPARE(positionChangedSpy.count(), 1);

    model.setClockDateTime(m_minTime); // FIXME crashes when using m_maxTime

    QCOMPARE(positionChangedSpy.count(), 2);

    delete plugin;
}

}

QTEST_MAIN(Marble::PlacemarkPositionProviderPluginTest)

#include "PlacemarkPositionProviderPluginTest.moc"
