///////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2021 Jon Beniston, M7RCE                                        //
// Copyright (C) 2020 Edouard Griffiths, F4EXB                                   //
//                                                                               //
// This program is free software; you can redistribute it and/or modify          //
// it under the terms of the GNU General Public License as published by          //
// the Free Software Foundation as version 3 of the License, or                  //
// (at your option) any later version.                                           //
//                                                                               //
// This program is distributed in the hope that it will be useful,               //
// but WITHOUT ANY WARRANTY; without even the implied warranty of                //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the                  //
// GNU General Public License V3 for more details.                               //
//                                                                               //
// You should have received a copy of the GNU General Public License             //
// along with this program. If not, see .          //
///////////////////////////////////////////////////////////////////////////////////
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#ifdef QT_WEBENGINE_FOUND
#include 
#include 
#include 
#endif
#include "feature/featureuiset.h"
#include "gui/basicfeaturesettingsdialog.h"
#include "gui/dialogpositioner.h"
#include "mainwindow.h"
#include "device/deviceuiset.h"
#include "util/units.h"
#include "util/maidenhead.h"
#include "util/morse.h"
#include "util/navtex.h"
#include "maplocationdialog.h"
#include "mapmaidenheaddialog.h"
#include "mapsettingsdialog.h"
#include "ibpbeacon.h"
#include "ui_mapgui.h"
#include "map.h"
#include "mapgui.h"
#include "SWGMapItem.h"
#include "SWGTargetAzimuthElevation.h"
MapGUI* MapGUI::create(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature)
{
    MapGUI* gui = new MapGUI(pluginAPI, featureUISet, feature);
    return gui;
}
void MapGUI::destroy()
{
    delete this;
}
void MapGUI::resetToDefaults()
{
    m_settings.resetToDefaults();
    displaySettings();
    applySettings(true);
}
QByteArray MapGUI::serialize() const
{
    return m_settings.serialize();
}
bool MapGUI::deserialize(const QByteArray& data)
{
    if (m_settings.deserialize(data))
    {
        m_feature->setWorkspaceIndex(m_settings.m_workspaceIndex);
        displaySettings();
        applySettings(true);
        return true;
    }
    else
    {
        resetToDefaults();
        return false;
    }
}
bool MapGUI::handleMessage(const Message& message)
{
    if (Map::MsgConfigureMap::match(message))
    {
        qDebug("MapGUI::handleMessage: Map::MsgConfigureMap");
        const Map::MsgConfigureMap& cfg = (Map::MsgConfigureMap&) message;
        if (cfg.getForce()) {
            m_settings = cfg.getSettings();
        } else {
            m_settings.applySettings(cfg.getSettingsKeys(), cfg.getSettings());
        }
        blockApplySettings(true);
        displaySettings();
        blockApplySettings(false);
        return true;
    }
    else if (Map::MsgReportAvailableChannelOrFeatures::match(message))
    {
        Map::MsgReportAvailableChannelOrFeatures& report = (Map::MsgReportAvailableChannelOrFeatures&) message;
        m_availableChannelOrFeatures = report.getItems();
        return true;
    }
    else if (Map::MsgFind::match(message))
    {
        Map::MsgFind& msgFind = (Map::MsgFind&) message;
        find(msgFind.getTarget());
        return true;
    }
    else if (Map::MsgSetDateTime::match(message))
    {
        Map::MsgSetDateTime& msgSetDateTime = (Map::MsgSetDateTime&) message;
        if (m_cesium) {
            m_cesium->setDateTime(msgSetDateTime.getDateTime());
        }
        return true;
    }
    else if (MainCore::MsgMapItem::match(message))
    {
        MainCore::MsgMapItem& msgMapItem = (MainCore::MsgMapItem&) message;
        SWGSDRangel::SWGMapItem *swgMapItem = msgMapItem.getSWGMapItem();
        // TODO: Could have this in SWGMapItem so plugins can create additional groups
        QString group;
        for (int i = 0; i < m_availableChannelOrFeatures.size(); i++)
        {
            if (m_availableChannelOrFeatures[i].m_source == msgMapItem.getPipeSource())
            {
                 for (int j = 0; j < MapSettings::m_pipeTypes.size(); j++)
                 {
                     if (m_availableChannelOrFeatures[i].m_type == MapSettings::m_pipeTypes[j]) {
                         group = m_availableChannelOrFeatures[i].m_type;
                     }
                 }
            }
        }
        update(msgMapItem.getPipeSource(), swgMapItem, group);
        return true;
    }
    return false;
}
void MapGUI::handleInputMessages()
{
    Message* message;
    while ((message = getInputMessageQueue()->pop()))
    {
        if (handleMessage(*message)) {
            delete message;
        }
    }
}
void MapGUI::onWidgetRolled(QWidget* widget, bool rollDown)
{
    (void) widget;
    (void) rollDown;
    getRollupContents()->saveState(m_rollupState);
}
MapGUI::MapGUI(PluginAPI* pluginAPI, FeatureUISet *featureUISet, Feature *feature, QWidget* parent) :
    FeatureGUI(parent),
    ui(new Ui::MapGUI),
    m_pluginAPI(pluginAPI),
    m_featureUISet(featureUISet),
    m_doApplySettings(true),
    m_objectMapModel(this),
    m_imageMapModel(this),
    m_polygonMapModel(this),
    m_polylineMapModel(this),
    m_beacons(nullptr),
    m_beaconDialog(this),
    m_ibpBeaconDialog(this),
    m_radioTimeDialog(this),
    m_cesium(nullptr)
{
    m_feature = feature;
    setAttribute(Qt::WA_DeleteOnClose, true);
    m_helpURL = "plugins/feature/map/readme.md";
    RollupContents *rollupContents = getRollupContents();
	ui->setupUi(rollupContents);
    setSizePolicy(rollupContents->sizePolicy());
    rollupContents->arrangeRollups();
	connect(rollupContents, SIGNAL(widgetRolled(QWidget*,bool)), this, SLOT(onWidgetRolled(QWidget*,bool)));
    // Enable MSAA antialiasing on 2D map
    // This can be much faster than using layer.smooth in the QML, when there are many items
    // However, only seems to work when set to 16, and doesn't seem to be supported on all graphics cards
    int multisamples = MainCore::instance()->getSettings().getMapMultisampling();
    if (multisamples > 0)
    {
        QSurfaceFormat format;
        format.setSamples(multisamples);
        ui->map->setFormat(format);
    }
    clearWikiMediaOSMCache();
    m_osmPort = 0;
    m_templateServer = new OSMTemplateServer(thunderforestAPIKey(), maptilerAPIKey(), m_osmPort);
    // Web server to serve dynamic files from QResources
    m_webPort = 0;
    m_webServer = new WebServer(m_webPort);
    ui->map->setAttribute(Qt::WA_AcceptTouchEvents, true);
    m_objectMapFilter.setSourceModel(&m_objectMapModel);
    m_imageMapFilter.setSourceModel(&m_imageMapModel);
    m_polygonMapFilter.setSourceModel(&m_polygonMapModel);
    m_polylineMapFilter.setSourceModel(&m_polylineMapModel);
    ui->map->rootContext()->setContextProperty("mapModelFiltered", &m_objectMapFilter);
    ui->map->rootContext()->setContextProperty("mapModel", &m_objectMapModel);
    ui->map->rootContext()->setContextProperty("imageModelFiltered", &m_imageMapFilter);
    ui->map->rootContext()->setContextProperty("polygonModelFiltered", &m_polygonMapFilter);
    ui->map->rootContext()->setContextProperty("polylineModelFiltered", &m_polylineMapFilter);
    connect(ui->map, &QQuickWidget::statusChanged, this, &MapGUI::statusChanged);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
    ui->map->setSource(QUrl(QStringLiteral("qrc:/map/map/map.qml")));
#else
    ui->map->setSource(QUrl(QStringLiteral("qrc:/map/map/map_6.qml")));
#endif
    m_settings.m_modelURL = QString("http://127.0.0.1:%1/3d/").arg(m_webPort);
    m_webServer->addPathSubstitution("3d", m_settings.m_modelDir);
    m_map = reinterpret_cast