#include "cmapview.h"

// qt头文件
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QFileInfo>
#include <QDragEnterEvent>
#include <QDropEvent>
#include <QMimeData>
#include <QDebug>
#include <QLabel>
#include <QMouseEvent>


// arcgis头文件
#include <LatitudeLongitudeGrid.h>
#include <CoordinateFormatter.h>
#include "LayerListModel.h"
#include "PictureMarkerSymbol.h"
#include "Graphic.h"
#include "SimpleFillSymbol.h"
#include "PolygonBuilder.h"
#include "WebTiledLayer.h"
#include "TileCache.h"
#include "ArcGISTiledLayer.h"
#include "RasterLayer.h"

#include "mapxwidget.h"
#include "NavigationFloatItem.h"

using namespace Esri::ArcGISRuntime;

CMapView::CMapView(QWidget *parent)
    : Esri::ArcGISRuntime::MapGraphicsView(parent)
{
    initUI();
    connects();
}

CMapView::CMapView(Esri::ArcGISRuntime::Map *map, QWidget *parent)
    : Esri::ArcGISRuntime::MapGraphicsView(map, parent)
{
    initUI(map);
    connects();
}

CMapView::~CMapView()
{
}

void CMapView::setLayerParam(LayerParam p)
{
    mLayerParam = p;
}

void CMapView::updateLayer()
{
    for(int i = 0; i < mLayerParam.layerNameList().size(); i++)
    {
        QString name = mLayerParam.layerNameList().at(i);
        QString url = mLayerParam.layerUrlList().at(i);
        LayerParam::LayerType type = mLayerParam.layerTypeList().at(i);

        switch (type)
        {
        case LayerParam::Tile:
        {
            Esri::ArcGISRuntime::WebTiledLayer* tiledLayer = new Esri::ArcGISRuntime::WebTiledLayer(url);
            tiledLayer->setName(name);
            mMap->operationalLayers()->append(tiledLayer);

            QVector<QVariant> data = {name};
            TreeItem* item = new TreeItem(data);
            mMapLayerTreeRootItem->addChildren(item);
            break;
        }
        case LayerParam::Raster:
        {
            if(!url.contains(":"))
            {
                url = Global::Instance()->configDirPath() + "/" + url;
            }

            QFileInfo info(url);
            if(info.suffix() == "tpk")
            {
                Esri::ArcGISRuntime::TileCache* tileCache = new Esri::ArcGISRuntime::TileCache(url, this);
                Esri::ArcGISRuntime::ArcGISTiledLayer* tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tileCache, this);
                mMap->operationalLayers()->append(tiledLayer);
                //tiledLayer->setVisible(false);
                tiledLayer->setName(name);
            }
            else if(info.suffix() == "tif")
            {
                Esri::ArcGISRuntime::Raster* tileCache = new Esri::ArcGISRuntime::Raster(url, this);
                Esri::ArcGISRuntime::RasterLayer* tiledLayer = new Esri::ArcGISRuntime::RasterLayer(tileCache, this);
                tiledLayer->setName(name);
                mMap->operationalLayers()->append(tiledLayer);
                qDebug() << url;
            }

            QVector<QVariant> data = {name};
            TreeItem* item = new TreeItem(data);
            mMapLayerTreeRootItem->addChildren(item);

            break;
        }
        case LayerParam::Encs:
        {
            QStringList encPaths = { url };
            mEncExchangeSet = new Esri::ArcGISRuntime::EncExchangeSet(encPaths);
            connect(mEncExchangeSet, &Esri::ArcGISRuntime::EncExchangeSet::doneLoading, this, [this](Esri::ArcGISRuntime::Error e)
                    {
                        if (!e.isEmpty())
                        {
                            qDebug() << "EncExchangeSet:error, " << e.message();
                            return;
                        }

                        // loop through datasets
                        const auto datasets = mEncExchangeSet->datasets();
                        int mEncLayerCount = datasets.size();
                        qDebug() << "ENC Layer count: " << mEncLayerCount;
                        for (Esri::ArcGISRuntime::EncDataset* dataset : datasets)
                        {
                            qDebug() << dataset->name();

                            // create an EncCell from each dataset
                            Esri::ArcGISRuntime::EncCell* cell = new Esri::ArcGISRuntime::EncCell(dataset, this);

                            // create an EncLayer from each cell
                            Esri::ArcGISRuntime::EncLayer* encLayer = new Esri::ArcGISRuntime::EncLayer(cell, this);
                            encLayer->setOpacity(0.8);

//                            QVector<QVariant> data = {encLayer->name()};
//                            TreeItem* item = new TreeItem(data);
//                            mMapLayerTreeRootItem->addChildren(item);

                            // connect to the doneLoading signal of the layer
                            connect(encLayer, &Esri::ArcGISRuntime::EncLayer::doneLoading, this, [this, encLayer](Esri::ArcGISRuntime::Error e)
                                    {
                                        if (!e.isEmpty())
                                        {
                                            qDebug() << "EncLayer:error, " << e.message();
                                            return;
                                        }

                                        m_extents << encLayer->fullExtent();

                                        if (m_extents.length() != mEncExchangeSet->datasets().length())
                                            return;

                                        // combine the extents
                                        Esri::ArcGISRuntime::Envelope fullExtent = Esri::ArcGISRuntime::GeometryEngine::combineExtents(m_extents);
                                        this->setViewpoint(fullExtent);
                                    });

                            // add the layer to the map
                            mMap->operationalLayers()->append(encLayer);
                        }
                    });
            mEncExchangeSet->load();

            QVector<QVariant> data = {name};
            TreeItem* item = new TreeItem(data);
            mMapLayerTreeRootItem->addChildren(item);

            break;
        }
        case LayerParam::Enc:
        {
            if(!url.contains(":"))
            {
                url = Global::Instance()->configDirPath() + "/" + url;
            }
            Esri::ArcGISRuntime::EncCell* cell = new Esri::ArcGISRuntime::EncCell(url, this);
            Esri::ArcGISRuntime::EncLayer* encLayer = new Esri::ArcGISRuntime::EncLayer(cell, this);
            encLayer->setOpacity(0.8);
            encLayer->setName(name);
            connect(encLayer, &Esri::ArcGISRuntime::EncLayer::doneLoading, this, [this, encLayer](Esri::ArcGISRuntime::Error e)
                    {
                        if (!e.isEmpty())
                        {
                            qDebug() << "EncLayer:error, " << e.message();
                            return;
                        }

                        // combine the extents
                        Esri::ArcGISRuntime::Envelope fullExtent = Esri::ArcGISRuntime::GeometryEngine::combineExtents(m_extents);
                        this->setViewpoint(fullExtent);
                    });

            // add the layer to the map
            mMap->operationalLayers()->append(encLayer);

            QVector<QVariant> data = {name};
            TreeItem* item = new TreeItem(data);
            mMapLayerTreeRootItem->addChildren(item);

            break;
        }
        case LayerParam::Shp:

            break;
        default:
            break;
        }
    }
}

void CMapView::openLocalMapFile(const QString &fileName)
{
    QFileInfo info(fileName);
    if(!info.exists())
    {
        emit signalTip(QString("%1文件不存在").arg(fileName), 1);
        return;
    }

    if(info.suffix() == "000")
    {
        Esri::ArcGISRuntime::EncCell* cell = new Esri::ArcGISRuntime::EncCell(fileName, this);
        Esri::ArcGISRuntime::EncLayer* encLayer = new Esri::ArcGISRuntime::EncLayer(cell, this);
        // encLayer->setOpacity(0.5);
        connect(encLayer, &Esri::ArcGISRuntime::EncLayer::doneLoading, this, [this, encLayer](Esri::ArcGISRuntime::Error e)
                {
                    if (!e.isEmpty())
                    {
                        qDebug() << "EncLayer:error, " << e.message();
                        return;
                    }

                    // combine the extents
                    Esri::ArcGISRuntime::Envelope fullExtent = Esri::ArcGISRuntime::GeometryEngine::combineExtents(m_extents);
                    this->setViewpoint(fullExtent);
                });

        // add the layer to the map
        mMap->operationalLayers()->append(encLayer);


    }
    else if(info.suffix() == "tpk")
    {
        Esri::ArcGISRuntime::TileCache* tileCache = new Esri::ArcGISRuntime::TileCache(fileName, this);
        Esri::ArcGISRuntime::ArcGISTiledLayer* tiledLayer = new Esri::ArcGISRuntime::ArcGISTiledLayer(tileCache, this);
        mMap->operationalLayers()->append(tiledLayer);
    }

}

void CMapView::setMapXWidgetVisible()
{
    mMapXWidget->setVisible(!mMapXWidget->isVisible());
}

void CMapView::setNavigationWidgetVisible()
{
    mNavigationWidget->setVisible(!mNavigationWidget->isVisible());
}

TreeItem *CMapView::mapLayerTreeRootItem()
{
    return mMapLayerTreeRootItem;
}

void CMapView::clearAllIcon()
{
    mArmyIconOverlay->graphics()->clear();
}

ArmyIconLayer *CMapView::armyIconLayer()
{
    return mArmyIconOverlay;
}

void CMapView::mapLayerVisible(const QString &name, bool state)
{
    Esri::ArcGISRuntime::LayerListModel* list = mMap->operationalLayers();
    for(auto i : *list)
    {
        if(i->name() == name)
        {
            i->setVisible(state);
        }
    }
}

bool CMapView::event(QEvent *e)
{
    if(e->type() == QEvent::Resize)
    {
        mNavigationWidget->move(this->width() - mNavigationWidget->width() - 10,
                                this->height() - mNavigationWidget->height() - 10);
    }
    return Esri::ArcGISRuntime::MapGraphicsView::event(e);
}

void CMapView::dragEnterEvent(QDragEnterEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else {
        event->ignore();
    }
}

void CMapView::dragMoveEvent(QDragMoveEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
        if (event->source() == this) {
            event->setDropAction(Qt::MoveAction);
            event->accept();
        } else {
            event->acceptProposedAction();
        }
    } else {
        event->ignore();
    }
}

void CMapView::dropEvent(QDropEvent *event)
{
    if (event->mimeData()->hasFormat("application/x-dnditemdata")) {
        QByteArray itemData = event->mimeData()->data("application/x-dnditemdata");
        QDataStream dataStream(&itemData, QIODevice::ReadOnly);

        QPixmap pixmap;
        QPoint offset;
        QString name;
        dataStream >> pixmap >> offset >> name;

//        QLabel *newIcon = new QLabel(this);
//        newIcon->setPixmap(pixmap);
//        newIcon->move(event->pos() - offset);
//        newIcon->show();
//        newIcon->setAttribute(Qt::WA_DeleteOnClose);

//        if (event->source() == this) {
//            event->setDropAction(Qt::MoveAction);
//            event->accept();
//        } else {
//            event->acceptProposedAction();
//        }
        QPoint p = event->pos();
        Esri::ArcGISRuntime::Point mapPoint(this->screenToLocation(p.x(), p.y()));
        QString str = Esri::ArcGISRuntime::CoordinateFormatter::toLatitudeLongitude(mapPoint, Esri::ArcGISRuntime::LatitudeLongitudeFormat::DecimalDegrees, 7);
        Esri::ArcGISRuntime::Point lla = Esri::ArcGISRuntime::CoordinateFormatter::fromLatitudeLongitude(str, Esri::ArcGISRuntime::SpatialReference::wgs84());
        //qDebug() << "lla:" << lla;

        Esri::ArcGISRuntime::PictureMarkerSymbol* iconSymbol = new Esri::ArcGISRuntime::PictureMarkerSymbol(pixmap.toImage());
        Esri::ArcGISRuntime::Point point(lla.x(), lla.y(), 10,
                    Esri::ArcGISRuntime::SpatialReference::wgs84());
        Esri::ArcGISRuntime::TextSymbol* txtSymbol = new Esri::ArcGISRuntime::TextSymbol(name, QColor("black"), 10,
                                               Esri::ArcGISRuntime::HorizontalAlignment::Left, Esri::ArcGISRuntime::VerticalAlignment::Middle, this);
        txtSymbol->setOffsetX(-15);
        txtSymbol->setOffsetY(-25);
        iconSymbol->setAngle(0);
        iconSymbol->setWidth(30);
        iconSymbol->setHeight(30);

        QVariantMap attr;
        attr[QLatin1String("GraphicType")] = QLatin1String("ArmyIcon");
        attr[QLatin1String("GraphicID")] = name;

//        Esri::ArcGISRuntime::SimpleMarkerSymbol* symbol = new Esri::ArcGISRuntime::SimpleMarkerSymbol;
//        symbol->setSize(50);
//        symbol->setStyle(Esri::ArcGISRuntime::SimpleMarkerSymbolStyle::Square);
//        //绘制带填充圆
//        //圆边框
//        SimpleLineSymbol *circleOutLine = new SimpleLineSymbol(SimpleLineSymbolStyle::Solid,QColor(Qt::red),2,this);
//        //圆填充
//        SimpleFillSymbol *circleSym = new SimpleFillSymbol(SimpleFillSymbolStyle::Solid,Qt::yellow,circleOutLine,this);
////        SimpleFillSymbol *sfs = new SimpleFillSymbol(SimpleFillSymbolStyle::DiagonalCross,"red",circleOutLine,this);
//        //m_sensorOverlay->setRenderer(new SimpleRenderer(circleSym));

//        //取点 根据圆心和半径
//        PolygonBuilder poly(SpatialReference::wgs84());
//        QGeoCoordinate *pnt = new QGeoCoordinate(25,120,100);
//        int step = 1;
//        for(int i = 0; i < 360; i += step)
//        {
//            double x = pnt->atDistanceAndAzimuth(
//                              1000000,i).longitude();
//            double y = pnt->atDistanceAndAzimuth(
//                              1000000,i).latitude();
//            double z = pnt->atDistanceAndAzimuth(
//                              1000000,i).altitude();
//            poly.addPoint(x,y,z);
//        }
//        //symbol->setColor(Qt::red);
//        Graphic *iconGraphic = new Graphic(poly.toGeometry(),circleSym,this);

        Esri::ArcGISRuntime::Graphic* iconGraphic = new Esri::ArcGISRuntime::Graphic(point, attr, iconSymbol);
        Esri::ArcGISRuntime::Graphic* textGraphic = new Esri::ArcGISRuntime::Graphic(point, txtSymbol);



        mArmyIconOverlay->graphics()->append(iconGraphic);
        mArmyIconOverlay->graphics()->append(textGraphic);

    } else {
        event->ignore();
    }
}

void CMapView::mouseMoveEvent(QMouseEvent *event)
{
    QPointF p = localPointToQPoint(event->localPos());
    emit mousePosToLLA(p);
    Esri::ArcGISRuntime::MapGraphicsView::mouseMoveEvent(event);
}


void CMapView::initUI(Esri::ArcGISRuntime::Map *map)
{
    QString dataPath = Global::Instance()->configDirPath();
    Esri::ArcGISRuntime::EncEnvironmentSettings::setResourcePath(dataPath + "/map/chart1/ArcGIS/Runtime/Data/ENC/hydrography");

    if(map == nullptr)
    {
        mMap = new Esri::ArcGISRuntime::Map;
    }
    else
    {
        mMap = map;
    }
    this->setAttributionTextVisible(false);
    this->setMap(mMap);

    m_currentScale = 40450000;
    m_maxScale = 81000000;
    m_minScale = 100000;
    m_viewPoint = Esri::ArcGISRuntime::Point(105, 2.5, Esri::ArcGISRuntime::SpatialReference::wgs84());
//    mMap->setMinScale(m_maxScale);
//    mMap->setMaxScale(m_minScale);
    mMap->setInitialViewpoint(Esri::ArcGISRuntime::Viewpoint(Esri::ArcGISRuntime::Point(105, 2.5), m_currentScale));

    mMapXWidget = new MapXWidget(this);
    //mMapXWidget->setFixedSize(300, 150);
    mMapXWidget->setFixedSize(180, 95);
    mMapXWidget->move(10, 5);

    mNavigationWidget = new NavigationFloatItem(this);

    QString title = "图层节点";
    QVector<QVariant> rootData;
    rootData << title;
    mMapLayerTreeRootItem = new TreeItem(rootData);

    mArmyIconOverlay = new ArmyIconLayer;
    mArmyIconOverlay->setMapView(this);
    this->graphicsOverlays()->append(mArmyIconOverlay);
    this->setMouseTracking(true);
}

void CMapView::connects()
{
    connect(this, SIGNAL(mapScaleChanged()), this, SLOT(onMapViewAndScaleChanged()));
    connect(this, SIGNAL(viewpointChanged()), this, SLOT(onMapViewAndScaleChanged()));
}

QPointF CMapView::localPointToQPoint(QPointF p)
{
    Esri::ArcGISRuntime::Point mapPoint(this->screenToLocation(p.x(), p.y()));
    QString str = Esri::ArcGISRuntime::CoordinateFormatter::toLatitudeLongitude(mapPoint, Esri::ArcGISRuntime::LatitudeLongitudeFormat::DecimalDegrees, 7);
    Esri::ArcGISRuntime::Point pnt = Esri::ArcGISRuntime::CoordinateFormatter::fromLatitudeLongitude(str, Esri::ArcGISRuntime::SpatialReference::wgs84());
    QPointF lla;
    lla.setX(pnt.x());// 经度
    lla.setY(pnt.y());// 纬度
    return lla;
}

Esri::ArcGISRuntime::Point CMapView::localPointToArcgisPoint(QPointF p)
{
    Esri::ArcGISRuntime::Point mapPoint(this->screenToLocation(p.x(), p.y()));
    QString str = Esri::ArcGISRuntime::CoordinateFormatter::toLatitudeLongitude(mapPoint, Esri::ArcGISRuntime::LatitudeLongitudeFormat::DecimalDegrees, 7);
    Esri::ArcGISRuntime::Point pnt = Esri::ArcGISRuntime::CoordinateFormatter::fromLatitudeLongitude(str, Esri::ArcGISRuntime::SpatialReference::wgs84());
    return pnt;
}

void CMapView::onMapViewAndScaleChanged()
{
    m_mapwidth = this->width();
    m_mapheight = this->height();
    // 采集地图当前显示状态下的四个顶点经纬坐标，调整鹰眼图的框选区域大小和位置
    QList<int> screen_X, screen_Y;
    screen_X << 0 << m_mapwidth << m_mapwidth << 0;
    screen_Y << 0 << 0 <<m_mapheight << m_mapheight;
    QList<Esri::ArcGISRuntime::Point> viewBoundaryPoint;
    viewBoundaryPoint.clear();
    for(int i = 0; i < 4; i++)
    {
        Esri::ArcGISRuntime::Point mapPoint(this->screenToLocation(screen_X.at(i), screen_Y.at(i)));
        QString str = Esri::ArcGISRuntime::CoordinateFormatter::toLatitudeLongitude(mapPoint, Esri::ArcGISRuntime::LatitudeLongitudeFormat::DecimalDegrees, 7);
        Esri::ArcGISRuntime::Point pnt = Esri::ArcGISRuntime::CoordinateFormatter::fromLatitudeLongitude(str, Esri::ArcGISRuntime::SpatialReference::wgs84());
        viewBoundaryPoint.append(pnt);
    }

    Esri::ArcGISRuntime::Point mapPoint(this->screenToLocation( m_mapwidth / 2, m_mapheight / 2));
    QString str = Esri::ArcGISRuntime::CoordinateFormatter::toLatitudeLongitude(mapPoint, Esri::ArcGISRuntime::LatitudeLongitudeFormat::DecimalDegrees, 7);
    m_viewPoint = Esri::ArcGISRuntime::CoordinateFormatter::fromLatitudeLongitude(str, Esri::ArcGISRuntime::SpatialReference::wgs84());

    m_currentScale = this->mapScale();

    QPointF p1;
    p1.setX(m_viewPoint.x() + 180);
    p1.setY(m_viewPoint.y() + 85);
    emit viewpoint(p1);

    QPointF p2;
    p2.setX(viewBoundaryPoint.at(0).x() + 180);
    p2.setY(viewBoundaryPoint.at(0).y() + 85);
    QPointF p3;
    p3.setX(viewBoundaryPoint.at(2).x() + 180);
    p3.setY(viewBoundaryPoint.at(2).y() + 85);

    mMapXWidget->setViewArea(p2, p3);
    mMapXWidget->setViewpoint(p1);
}

//void CMapView::changeLayer(int index)
//{
//    QString name = mLayerNames.at(index);
//    int size = mMap->operationalLayers()->size();
//    int tmpIndex = -1;
//    for(int i = 0; i < size; i++)
//    {
//        Esri::ArcGISRuntime::Layer* layer = mMap->operationalLayers()->at(i);
//        if(layer->name() == name)
//        {
//            tmpIndex = i;
//            break;
//        }
//    }

//    if(tmpIndex != -1)
//    {
//        mMap->operationalLayers()->move(tmpIndex, size - mEncLayerCount - 1);
//    }

//}
