// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2010 Wes Hardaker <hardaker@users.sourceforge.net>
//

#include "AprsPlugin.h"

#include "MarbleDebug.h"
#include "MarbleGlobal.h"

#include <QAction>
#include <QColor>
#include <QMutexLocker>
#include <QTcpSocket>

#include "AprsFile.h"
#include "AprsTCPIP.h"
#include "GeoDataCoordinates.h"
#include "GeoPainter.h"
#include "MarbleModel.h"
#include "MarbleWidget.h"
#include "ViewportParams.h"

#include <aprsconfig.h>

#if HAVE_QTSERIALPORT
#include "AprsTTY.h"
#endif

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

AprsPlugin::AprsPlugin(const MarbleModel *marbleModel)
    : RenderPlugin(marbleModel)
    , m_mutex(new QMutex)
    , m_initialized(false)
    , m_tcpipGatherer(nullptr)
    , m_ttyGatherer(nullptr)
    , m_fileGatherer(nullptr)
    , m_action(nullptr)
    , m_useInternet(true)
    , m_useTty(false)
    , m_useFile(false)
    , m_aprsHost(QStringLiteral("rotate.aprs.net"))
    , m_aprsPort(10253)
    , m_tncTty(QStringLiteral("/dev/ttyUSB0"))
    , m_aprsFile()
    , m_dumpTcpIp(false)
    , m_dumpTty(false)
    , m_dumpFile(false)
    , m_fadeTime(10)
    , m_hideTime(45)
    , m_configDialog(nullptr)
    , ui_configWidget(nullptr)
{
    setEnabled(true);
    setVisible(false);

    setSettings(QHash<QString, QVariant>());

    connect(this, &RenderPlugin::visibilityChanged, this, &AprsPlugin::updateVisibility);

    m_action = new QAction(this);
    connect(m_action, &QAction::toggled, this, &RenderPlugin::setVisible);
}

AprsPlugin::~AprsPlugin()
{
    stopGatherers();

    delete m_configDialog;
    delete ui_configWidget;

    QMap<QString, AprsObject *>::Iterator obj;
    QMap<QString, AprsObject *>::Iterator end = m_objects.end();

    for (obj = m_objects.begin(); obj != end; ++obj) {
        delete *obj;
    }

    m_objects.clear();

    delete m_mutex;
}

void AprsPlugin::updateVisibility(bool visible)
{
    if (visible)
        restartGatherers();
    else
        stopGatherers();
}

RenderPlugin::RenderType AprsPlugin::renderType() const
{
    return OnlineRenderType;
}

QStringList AprsPlugin::backendTypes() const
{
    return QStringList(QStringLiteral("aprs"));
}

QString AprsPlugin::renderPolicy() const
{
    return QStringLiteral("ALWAYS");
}

QStringList AprsPlugin::renderPosition() const
{
    return QStringList(QStringLiteral("HOVERS_ABOVE_SURFACE"));
}

QString AprsPlugin::name() const
{
    return tr("Amateur Radio Aprs Plugin");
}

QString AprsPlugin::guiString() const
{
    return tr("Amateur Radio &Aprs Plugin");
}

QString AprsPlugin::nameId() const
{
    return QStringLiteral("aprs-plugin");
}

QString AprsPlugin::version() const
{
    return QStringLiteral("1.0");
}

QString AprsPlugin::description() const
{
    return tr("This plugin displays APRS data gleaned from the Internet.  APRS is an Amateur Radio protocol for broadcasting location and other information.");
}

QString AprsPlugin::copyrightYears() const
{
    return QStringLiteral("2009, 2010");
}

QList<PluginAuthor> AprsPlugin::pluginAuthors() const
{
    return QList<PluginAuthor>() << PluginAuthor(QStringLiteral("Wes Hardaker"), QStringLiteral("hardaker@users.sourceforge.net"));
}

QIcon AprsPlugin::icon() const
{
    return QIcon(QStringLiteral(":/icons/aprs.png"));
}

void AprsPlugin::stopGatherers()
{
    // tell them all to stop
    if (m_tcpipGatherer)
        m_tcpipGatherer->shutDown();

#if HAVE_QTSERIALPORT
    if (m_ttyGatherer)
        m_ttyGatherer->shutDown();
#endif

    if (m_fileGatherer)
        m_fileGatherer->shutDown();

    // now wait for them for at least 2 seconds (it shouldn't take that long)
    if (m_tcpipGatherer)
        if (m_tcpipGatherer->wait(2000))
            delete m_tcpipGatherer;

#if HAVE_QTSERIALPORT
    if (m_ttyGatherer)
        if (m_ttyGatherer->wait(2000))
            delete m_ttyGatherer;
#endif

    if (m_fileGatherer)
        if (m_fileGatherer->wait(2000))
            delete m_fileGatherer;

    m_tcpipGatherer = nullptr;
    m_ttyGatherer = nullptr;
    m_fileGatherer = nullptr;
}

void AprsPlugin::restartGatherers()
{
    stopGatherers();

    if (m_useInternet) {
        m_tcpipGatherer = new AprsGatherer(new AprsTCPIP(m_aprsHost, m_aprsPort), &m_objects, m_mutex, &m_filter);
        m_tcpipGatherer->setSeenFrom(GeoAprsCoordinates::FromTCPIP);
        m_tcpipGatherer->setDumpOutput(m_dumpTcpIp);

        m_tcpipGatherer->start();
        mDebug() << "started TCPIP gatherer";
    }

#if HAVE_QTSERIALPORT
    if (m_useTty) {
        m_ttyGatherer = new AprsGatherer(new AprsTTY(m_tncTty), &m_objects, m_mutex, nullptr);

        m_ttyGatherer->setSeenFrom(GeoAprsCoordinates::FromTTY);
        m_ttyGatherer->setDumpOutput(m_dumpTty);

        m_ttyGatherer->start();
        mDebug() << "started TTY gatherer";
    }
#endif

    if (m_useFile) {
        m_fileGatherer = new AprsGatherer(new AprsFile(m_aprsFile), &m_objects, m_mutex, nullptr);

        m_fileGatherer->setSeenFrom(GeoAprsCoordinates::FromFile);
        m_fileGatherer->setDumpOutput(m_dumpFile);

        m_fileGatherer->start();
        mDebug() << "started File gatherer";
    }
}

void AprsPlugin::initialize()
{
    m_initialized = true;
    mDebug() << "APRS initialized";

    restartGatherers();
}

QDialog *AprsPlugin::configDialog()
{
    if (!m_configDialog) {
        // Initializing configuration dialog
        m_configDialog = new QDialog();
        ui_configWidget = new Ui::AprsConfigWidget;
        ui_configWidget->setupUi(m_configDialog);
        readSettings();
        connect(ui_configWidget->m_buttonBox, &QDialogButtonBox::accepted, this, &AprsPlugin::writeSettings);
        connect(ui_configWidget->m_buttonBox, &QDialogButtonBox::rejected, this, &AprsPlugin::readSettings);
        //       QPushButton *applyButton =
        //             ui_configWidget->m_buttonBox->button( QDialogButtonBox::Apply );
        //         connect( applyButton, SIGNAL(clicked()),
        //                  this,        SLOT(writeSettings()) );
    }
    return m_configDialog;
}

void AprsPlugin::readSettings()
{
    if (!m_configDialog) {
        return;
    }

#ifndef HAVE_QTSERIALPORT
    ui_configWidget->tabWidget->setTabEnabled(ui_configWidget->tabWidget->indexOf(ui_configWidget->Device), false);
#endif

    // Connect to the net?
    if (m_useInternet)
        ui_configWidget->m_internetBox->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_internetBox->setCheckState(Qt::Unchecked);

    // Connection Information
    ui_configWidget->m_serverName->setText(m_aprsHost);
    ui_configWidget->m_serverPort->setText(QString::number(m_aprsPort));

    // Read from a TTY serial port?
    if (m_useTty)
        ui_configWidget->m_serialBox->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_serialBox->setCheckState(Qt::Unchecked);

    // Serial port to use
    ui_configWidget->m_ttyName->setText(m_tncTty);

    // Read from a File?
    if (m_useFile)
        ui_configWidget->m_useFile->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_useFile->setCheckState(Qt::Unchecked);

    // Serial port to use
    ui_configWidget->m_fileName->setText(m_aprsFile);

    // Dumping settings
    if (m_dumpTcpIp)
        ui_configWidget->m_tcpipdump->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_tcpipdump->setCheckState(Qt::Unchecked);

    if (m_dumpTty)
        ui_configWidget->m_ttydump->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_ttydump->setCheckState(Qt::Unchecked);

    if (m_dumpFile)
        ui_configWidget->m_filedump->setCheckState(Qt::Checked);
    else
        ui_configWidget->m_filedump->setCheckState(Qt::Unchecked);

    // display settings
    ui_configWidget->m_fadetime->setText(QString::number(m_fadeTime));
    ui_configWidget->m_hidetime->setText(QString::number(m_hideTime));
}

void AprsPlugin::writeSettings()
{
    m_useInternet = ui_configWidget->m_internetBox->checkState() == Qt::Checked;
    m_useTty = ui_configWidget->m_serialBox->checkState() == Qt::Checked;
    m_useFile = ui_configWidget->m_useFile->checkState() == Qt::Checked;

    m_aprsHost = ui_configWidget->m_serverName->text();
    m_aprsPort = ui_configWidget->m_serverPort->text().toInt();
    m_tncTty = ui_configWidget->m_ttyName->text();

    m_dumpTcpIp = ui_configWidget->m_tcpipdump->checkState() == Qt::Checked;
    m_dumpTty = ui_configWidget->m_ttydump->checkState() == Qt::Checked;
    m_dumpFile = ui_configWidget->m_filedump->checkState() == Qt::Checked;

    m_fadeTime = ui_configWidget->m_fadetime->text().toInt();
    m_hideTime = ui_configWidget->m_hidetime->text().toInt();

    restartGatherers();
    Q_EMIT settingsChanged(nameId());
}

QHash<QString, QVariant> AprsPlugin::settings() const
{
    QHash<QString, QVariant> result = RenderPlugin::settings();

    result.insert(QStringLiteral("useInternet"), m_useInternet);
    result.insert(QStringLiteral("useTTY"), m_useTty);
    result.insert(QStringLiteral("useFile"), m_useFile);
    result.insert(QStringLiteral("APRSHost"), m_aprsHost);
    result.insert(QStringLiteral("APRSPort"), m_aprsPort);
    result.insert(QStringLiteral("TNCTTY"), m_tncTty);
    result.insert(QStringLiteral("FileName"), m_aprsFile);
    result.insert(QStringLiteral("TCPIPDump"), m_dumpTcpIp);
    result.insert(QStringLiteral("TTYDump"), m_dumpTty);
    result.insert(QStringLiteral("FileDump"), m_dumpFile);
    result.insert(QStringLiteral("fadeTime"), m_fadeTime);
    result.insert(QStringLiteral("hideTime"), m_hideTime);

    return result;
}

void AprsPlugin::setSettings(const QHash<QString, QVariant> &settings)
{
    RenderPlugin::setSettings(settings);

    m_useInternet = settings.value(QStringLiteral("useInternet"), true).toBool();
    m_useTty = settings.value(QStringLiteral("useTTY"), false).toBool();
    m_useFile = settings.value(QStringLiteral("useFile"), false).toBool();

    m_aprsHost = settings.value(QStringLiteral("APRSHost"), QStringLiteral("rotate.aprs.net")).toString();
    m_aprsPort = settings.value(QStringLiteral("APRSPort"), 10253).toInt();
    m_tncTty = settings.value(QStringLiteral("TNCTTY"), QStringLiteral("/dev/ttyUSB0")).toString();
    m_aprsFile = settings.value(QStringLiteral("FileName"), QString()).toString();

    m_dumpTcpIp = settings.value(QStringLiteral("TCPIPDump"), false).toBool();
    m_dumpTty = settings.value(QStringLiteral("TTYDump"), false).toBool();
    m_dumpFile = settings.value(QStringLiteral("FileDump"), false).toBool();

    m_fadeTime = settings.value(QStringLiteral("fadeTime"), 10).toInt();
    m_hideTime = settings.value(QStringLiteral("hideTime"), 45).toInt();

    readSettings();
    Q_EMIT settingsChanged(nameId());
}

bool AprsPlugin::isInitialized() const
{
    return m_initialized;
}

bool AprsPlugin::render(GeoPainter *painter, ViewportParams *viewport, const QString &renderPos, GeoSceneLayer *layer)
{
    Q_UNUSED(renderPos)
    Q_UNUSED(layer)

    int fadetime = m_fadeTime * 60000;
    int hidetime = m_hideTime * 60000;

    painter->save();

    if (!(viewport->viewLatLonAltBox() == m_lastBox)) {
        m_lastBox = viewport->viewLatLonAltBox();
        QString towrite = QLatin1StringView("#filter a/") + QString::number(m_lastBox.north(GeoDataCoordinates::Degree)) + QLatin1Char('/')
            + QString::number(m_lastBox.west(GeoDataCoordinates::Degree)) + QLatin1Char('/') + QString::number(m_lastBox.south(GeoDataCoordinates::Degree))
            + QLatin1Char('/') + QString::number(m_lastBox.east(GeoDataCoordinates::Degree)) + QLatin1Char('\n');
        mDebug() << "upating filter: " << towrite.toLocal8Bit().data();

        QMutexLocker locker(m_mutex);
        m_filter = towrite;
    }

    QMutexLocker locker(m_mutex);
    QMap<QString, AprsObject *>::ConstIterator obj;
    for (obj = m_objects.constBegin(); obj != m_objects.constEnd(); ++obj) {
        (*obj)->render(painter, viewport, fadetime, hidetime);
    }

    painter->restore();

    return true;
}

QAction *AprsPlugin::action() const
{
    m_action->setCheckable(true);
    m_action->setChecked(visible());
    m_action->setIcon(icon());
    m_action->setText(guiString());
    m_action->setToolTip(description());
    return m_action;
}

#include "moc_AprsPlugin.cpp"
