// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2009 Eckhart Wörner <ewoerner@kde.org>
// SPDX-FileCopyrightText: 2011 Bastian Holst <bastianholst@gmx.de>
//

#include "GpsdConnection.h"

#include "MarbleDebug.h"

#include <QElapsedTimer>
#include <QTime>

#include <cerrno>
#include <clocale>

using namespace Marble;
/* TRANSLATOR Marble::GpsdConnection */

const int gpsUpdateInterval = 1000; // ms
const int gpsWaitTimeout = 200; // ms

GpsdConnection::GpsdConnection(QObject *parent)
    : QObject(parent)
    ,
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 5)
    m_gpsd("localhost", DEFAULT_GPSD_PORT)
    ,
#endif
    m_timer(nullptr)
{
    m_oldLocale = setlocale(LC_NUMERIC, nullptr);
    setlocale(LC_NUMERIC, "C");
    connect(&m_timer, SIGNAL(timeout()), this, SLOT(update()));
}

GpsdConnection::~GpsdConnection()
{
    setlocale(LC_NUMERIC, m_oldLocale);
}

void GpsdConnection::initialize()
{
    m_timer.stop();
    bool success = false;
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 5)
    success = true;
#else
    gps_data_t *data = m_gpsd.open();
    success = (data != 0);
#endif
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 3) && defined(WATCH_ENABLE)
    if (success) {
        success = (m_gpsd.stream(WATCH_ENABLE) != nullptr);
    }
#endif
    if (success) {
        m_status = PositionProviderStatusAcquiring;
        emit statusChanged(m_status);
        m_timer.start(gpsUpdateInterval);
    } else {
        // There is also gps_errstr() for libgps version >= 2.90,
        // but it doesn't return a sensible error description
        switch (errno) {
        case NL_NOSERVICE:
            m_error = tr("Internal gpsd error (cannot get service entry)");
            break;
        case NL_NOHOST:
            m_error = tr("Internal gpsd error (cannot get host entry)");
            break;
        case NL_NOPROTO:
            m_error = tr("Internal gpsd error (cannot get protocol entry)");
            break;
        case NL_NOSOCK:
            m_error = tr("Internal gpsd error (unable to create socket)");
            break;
        case NL_NOSOCKOPT:
            m_error = tr("Internal gpsd error (unable to set socket option)");
            break;
        case NL_NOCONNECT:
            m_error = tr("No GPS device found by gpsd.");
            break;
        default:
            m_error = tr("Unknown error when opening gpsd connection");
            break;
        }

        m_status = PositionProviderStatusError;
        emit statusChanged(m_status);

        mDebug() << "Connection to gpsd failed, no position info available: " << m_error;
    }
}

void GpsdConnection::update()
{
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 12) && defined(PACKET_SET)
    gps_data_t *data = nullptr;

    while (m_gpsd.waiting(0)) {
        gps_data_t *currentData = m_gpsd.read();

        if (currentData && currentData->set & PACKET_SET) {
            data = currentData;
        }
    }

    if (data) {
        emit gpsdInfo(*data);
    }
#else
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 4) && defined(PACKET_SET)
    gps_data_t *data = nullptr;

    QElapsedTimer watchdog;
    watchdog.start();

#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION >= 5)
    while (m_gpsd.waiting(0) && watchdog.elapsed() < gpsWaitTimeout) {
        gps_data_t *currentData = m_gpsd.read();
#else
    while (m_gpsd.waiting() && watchdog.elapsed() < gpsWaitTimeout) {
        gps_data_t *currentData = m_gpsd.poll();
#endif

        if (currentData && currentData->set & PACKET_SET) {
            data = currentData;
        }
    }

    if (data) {
        emit gpsdInfo(*data);
    }
#else
#if defined(GPSD_API_MAJOR_VERSION) && (GPSD_API_MAJOR_VERSION == 3) && defined(PACKET_SET)
    gps_data_t *data = m_gpsd.poll();
#else
    gps_data_t *data = m_gpsd.query("o");
#endif

    if (data) {
        emit gpsdInfo(*data);
    } else if (m_status != PositionProviderStatusAcquiring) {
        mDebug() << "Lost connection to gpsd, trying to re-open.";
        initialize();
    }
#endif
#endif
}

QString GpsdConnection::error() const
{
    return m_error;
}

#include "moc_GpsdConnection.cpp"
