// SPDX-License-Identifier: LGPL-2.1-or-later
//
// SPDX-FileCopyrightText: 2004-2007 Torsten Rahn <tackat@kde.org>
// SPDX-FileCopyrightText: 2007 Inge Wallin <ingwa@kde.org>
// SPDX-FileCopyrightText: 2008-2009 Patrick Spendrin <ps_ml@gmx.de>
//

// Own
#include "GeoDataPlacemark.h"

// Private
#include "GeoDataPlacemark_p.h"

#include "GeoDataData.h"
#include "GeoDataLatLonAltBox.h"
#include "GeoDataMultiGeometry.h"
#include "osm/OsmPlacemarkData.h"

// Qt
#include "GeoDataModel.h"
#include "GeoDataTrack.h"
#include "MarbleDebug.h"
#include <QDataStream>
#include <QString>
#include <QStringRef>
#include <QXmlStreamWriter>

namespace Marble
{

const OsmPlacemarkData GeoDataPlacemarkPrivate::s_nullOsmPlacemarkData = OsmPlacemarkData();
const GeoDataPlacemarkExtendedData GeoDataPlacemarkPrivate::s_nullPlacemarkExtendedData = GeoDataPlacemarkExtendedData();

GeoDataPlacemark::GeoDataPlacemark()
    : GeoDataFeature(new GeoDataPlacemarkPrivate)
{
    Q_D(GeoDataPlacemark);
    d->m_geometry->setParent(this);
}

GeoDataPlacemark::GeoDataPlacemark(const GeoDataPlacemark &other)
    : GeoDataFeature(other, new GeoDataPlacemarkPrivate(*other.d_func()))
{
    Q_D(GeoDataPlacemark);
    if (d->m_geometry) {
        d->m_geometry->setParent(this);
    }
}

GeoDataPlacemark::GeoDataPlacemark(const QString &name)
    : GeoDataFeature(new GeoDataPlacemarkPrivate)
{
    Q_D(GeoDataPlacemark);
    d->m_name = name;
    d->m_geometry->setParent(this);
}

GeoDataPlacemark::~GeoDataPlacemark()
{
    // nothing to do
}

GeoDataPlacemark &GeoDataPlacemark::operator=(const GeoDataPlacemark &other)
{
    if (this != &other) {
        Q_D(GeoDataPlacemark);
        *d = *other.d_func();
        if (d->m_geometry) {
            d->m_geometry->setParent(this);
        }
    }

    return *this;
}

bool GeoDataPlacemark::operator==(const GeoDataPlacemark &other) const
{
    Q_D(const GeoDataPlacemark);
    const GeoDataPlacemarkPrivate *const other_d = other.d_func();
    if (!equals(other) || d->m_population != other_d->m_population) {
        return false;
    }

    if ((d->m_placemarkExtendedData && !other_d->m_placemarkExtendedData) && (*d->m_placemarkExtendedData != GeoDataPlacemarkExtendedData())) {
        return false;
    }

    if ((!d->m_placemarkExtendedData && other_d->m_placemarkExtendedData) && (GeoDataPlacemarkExtendedData() != *other_d->m_placemarkExtendedData)) {
        return false;
    }

    if (d->m_placemarkExtendedData && other_d->m_placemarkExtendedData && !(*d->m_placemarkExtendedData == *other_d->m_placemarkExtendedData)) {
        return false;
    }

    if (!d->m_geometry && !other_d->m_geometry) {
        return true;
    }
    if ((!d->m_geometry && other_d->m_geometry) || (d->m_geometry && !other_d->m_geometry)) {
        return false;
    }

    if (*d->m_geometry != *other_d->m_geometry) {
        return false;
    }

    return true;
}

bool GeoDataPlacemark::operator!=(const GeoDataPlacemark &other) const
{
    return !this->operator==(other);
}

const char *GeoDataPlacemark::nodeType() const
{
    return GeoDataTypes::GeoDataPlacemarkType;
}

GeoDataFeature *GeoDataPlacemark::clone() const
{
    return new GeoDataPlacemark(*this);
}

GeoDataPlacemark::GeoDataVisualCategory GeoDataPlacemark::visualCategory() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_visualCategory;
}

void GeoDataPlacemark::setVisualCategory(GeoDataPlacemark::GeoDataVisualCategory index)
{
    Q_D(GeoDataPlacemark);
    d->m_visualCategory = index;
}

GeoDataGeometry *GeoDataPlacemark::geometry()
{
    Q_D(GeoDataPlacemark);
    return d->m_geometry;
}

const GeoDataGeometry *GeoDataPlacemark::geometry() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_geometry;
}

const OsmPlacemarkData &GeoDataPlacemark::osmData() const
{
    Q_D(const GeoDataPlacemark);
    return d->osmPlacemarkData();
}

void GeoDataPlacemark::setOsmData(const OsmPlacemarkData &osmData)
{
    Q_D(GeoDataPlacemark);
    d->osmPlacemarkData() = osmData;
}

OsmPlacemarkData &GeoDataPlacemark::osmData()
{
    Q_D(GeoDataPlacemark);
    return d->osmPlacemarkData();
}

bool GeoDataPlacemark::hasOsmData() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_osmPlacemarkData != nullptr;
}

void GeoDataPlacemark::clearOsmData()
{
    Q_D(GeoDataPlacemark);
    delete d->m_osmPlacemarkData;
    d->m_osmPlacemarkData = nullptr;
}

const GeoDataLookAt *GeoDataPlacemark::lookAt() const
{
    return dynamic_cast<const GeoDataLookAt *>(abstractView());
}

GeoDataLookAt *GeoDataPlacemark::lookAt()
{
    return dynamic_cast<GeoDataLookAt *>(abstractView());
}

bool GeoDataPlacemark::placemarkLayoutOrderCompare(const GeoDataPlacemark *left, const GeoDataPlacemark *right)
{
    const GeoDataPlacemarkPrivate *const left_d = left->d_func();
    const GeoDataPlacemarkPrivate *const right_d = right->d_func();

    if (left_d->m_zoomLevel != right_d->m_zoomLevel) {
        return (left_d->m_zoomLevel < right_d->m_zoomLevel); // lower zoom level comes first
    }

    if (left_d->m_popularity != right_d->m_popularity) {
        return (left_d->m_popularity > right_d->m_popularity); // higher popularity comes first
    }

    return left < right; // lower pointer value comes first
}

GeoDataCoordinates GeoDataPlacemark::coordinate(const QDateTime &dateTime, bool *iconAtCoordinates) const
{
    Q_D(const GeoDataPlacemark);
    bool hasIcon = false;
    GeoDataCoordinates coord;

    if (d->m_geometry) {
        // Beware: comparison between pointers, not strings.
        if (geodata_cast<GeoDataPoint>(d->m_geometry) || geodata_cast<GeoDataPolygon>(d->m_geometry) || geodata_cast<GeoDataLinearRing>(d->m_geometry)) {
            hasIcon = true;
            coord = d->m_geometry->latLonAltBox().center();
        } else if (const auto multiGeometry = geodata_cast<GeoDataMultiGeometry>(d->m_geometry)) {
            QList<GeoDataGeometry *>::ConstIterator it = multiGeometry->constBegin();
            QList<GeoDataGeometry *>::ConstIterator end = multiGeometry->constEnd();
            for (; it != end; ++it) {
                if (geodata_cast<GeoDataPoint>(*it) || geodata_cast<GeoDataPolygon>(*it) || geodata_cast<GeoDataLinearRing>(*it)) {
                    hasIcon = true;
                    break;
                }
            }

            coord = d->m_geometry->latLonAltBox().center();
        } else if (const auto track = geodata_cast<GeoDataTrack>(d->m_geometry)) {
            hasIcon = track->size() != 0 && track->firstWhen() <= dateTime;
            coord = track->coordinatesAt(dateTime);
        } else if (const auto lineString = geodata_cast<GeoDataLineString>(d->m_geometry)) {
            auto const size = lineString->size();
            if (size == 0) {
                return {};
            } else if (size < 3) {
                // Approximate center if there are just two coordinates
                return lineString->latLonAltBox().center();
            } else {
                return lineString->at(size / 2);
            }
        } else {
            coord = d->m_geometry->latLonAltBox().center();
        }
    }

    if (iconAtCoordinates != nullptr) {
        *iconAtCoordinates = hasIcon;
    }
    return coord;
}

void GeoDataPlacemark::coordinate(qreal &lon, qreal &lat, qreal &alt) const
{
    coordinate().geoCoordinates(lon, lat, alt);
}

void GeoDataPlacemark::setCoordinate(qreal lon, qreal lat, qreal alt, GeoDataPoint::Unit _unit)
{
    setGeometry(new GeoDataPoint(lon, lat, alt, _unit));
}

void GeoDataPlacemark::setCoordinate(const GeoDataCoordinates &point)
{
    setGeometry(new GeoDataPoint(point));
}

void GeoDataPlacemark::setGeometry(GeoDataGeometry *entry)
{
    Q_D(GeoDataPlacemark);
    delete d->m_geometry;
    d->m_geometry = entry;
    d->m_geometry->setParent(this);
}

QString GeoDataPlacemark::displayName() const
{
    if (hasOsmData()) {
        OsmPlacemarkData const &data = osmData();
        QStringList const uiLanguages = QLocale::system().uiLanguages();
        for (const QString &uiLanguage : uiLanguages) {
            for (auto tagIter = data.tagsBegin(), end = data.tagsEnd(); tagIter != end; ++tagIter) {
                if (tagIter.key().startsWith(QLatin1StringView("name:"))) {
                    QString const tagLanguage = tagIter.key().mid(5);
                    if (tagLanguage == uiLanguage) {
                        return tagIter.value();
                    }
                }
            }
        }
    }

    return name();
}

QString GeoDataPlacemark::categoryName() const
{
    Q_D(const GeoDataPlacemark);
    switch (d->m_visualCategory) {
    case Valley:
        return GeoDataPlacemarkPrivate::tr("Valley");
    case OtherTerrain:
        return GeoDataPlacemarkPrivate::tr("Terrain");
    case Crater:
        return GeoDataPlacemarkPrivate::tr("Crater");
    case Mare:
        return GeoDataPlacemarkPrivate::tr("Sea");
    case MannedLandingSite:
        return GeoDataPlacemarkPrivate::tr("Manned Landing Site");
    case RoboticRover:
        return GeoDataPlacemarkPrivate::tr("Robotic Rover");
    case UnmannedSoftLandingSite:
        return GeoDataPlacemarkPrivate::tr("Unmanned Soft Landing Site");
    case UnmannedHardLandingSite:
        return GeoDataPlacemarkPrivate::tr("Unmanned Hard Landing Site");
    case Mons:
        return GeoDataPlacemarkPrivate::tr("Mountain");
    case SmallCity:
        return GeoDataPlacemarkPrivate::tr("City");
    case SmallCountyCapital:
        return GeoDataPlacemarkPrivate::tr("County Capital");
    case SmallStateCapital:
        return GeoDataPlacemarkPrivate::tr("State Capital");
    case SmallNationCapital:
        return GeoDataPlacemarkPrivate::tr("Nation Capital");
    case MediumCity:
        return GeoDataPlacemarkPrivate::tr("City");
    case MediumCountyCapital:
        return GeoDataPlacemarkPrivate::tr("County Capital");
    case MediumStateCapital:
        return GeoDataPlacemarkPrivate::tr("State Capital");
    case MediumNationCapital:
        return GeoDataPlacemarkPrivate::tr("Nation Capital");
    case BigCity:
        return GeoDataPlacemarkPrivate::tr("City");
    case BigCountyCapital:
        return GeoDataPlacemarkPrivate::tr("County Capital");
    case BigStateCapital:
        return GeoDataPlacemarkPrivate::tr("State Capital");
    case BigNationCapital:
        return GeoDataPlacemarkPrivate::tr("Nation Capital");
    case LargeCity:
        return GeoDataPlacemarkPrivate::tr("City");
    case LargeCountyCapital:
        return GeoDataPlacemarkPrivate::tr("County Capital");
    case LargeStateCapital:
        return GeoDataPlacemarkPrivate::tr("State Capital");
    case LargeNationCapital:
        return GeoDataPlacemarkPrivate::tr("Nation Capital");
    case Nation:
        return GeoDataPlacemarkPrivate::tr("Nation");
    case Mountain:
        return GeoDataPlacemarkPrivate::tr("Mountain");
    case Volcano:
        return GeoDataPlacemarkPrivate::tr("Volcano");
    case Continent:
        return GeoDataPlacemarkPrivate::tr("Continent");
    case Ocean:
        return GeoDataPlacemarkPrivate::tr("Ocean");
    case GeographicPole:
        return GeoDataPlacemarkPrivate::tr("Geographic Pole");
    case MagneticPole:
        return GeoDataPlacemarkPrivate::tr("Magnetic Pole");
    case ShipWreck:
        return GeoDataPlacemarkPrivate::tr("Ship Wreck");
    case AirPort:
        return GeoDataPlacemarkPrivate::tr("Air Port");
    case Observatory:
        return GeoDataPlacemarkPrivate::tr("Observatory");
    case MilitaryDangerArea:
        return GeoDataPlacemarkPrivate::tr("Military Danger Area");
    case OsmSite:
        return GeoDataPlacemarkPrivate::tr("OSM Site");
    case Coordinate:
        return GeoDataPlacemarkPrivate::tr("Coordinate");
    case Bookmark:
        return GeoDataPlacemarkPrivate::tr("Bookmark");
    case Satellite:
        return GeoDataPlacemarkPrivate::tr("Satellite");

    // OpenStreetMap categories
    case PlaceCity:
        return GeoDataPlacemarkPrivate::tr("City");
    case PlaceCityCapital:
        return GeoDataPlacemarkPrivate::tr("City Capital");
    case PlaceCityNationalCapital:
        return GeoDataPlacemarkPrivate::tr("National Capital");
    case PlaceSuburb:
        return GeoDataPlacemarkPrivate::tr("Suburb");
    case PlaceHamlet:
        return GeoDataPlacemarkPrivate::tr("Hamlet");
    case PlaceLocality:
        return GeoDataPlacemarkPrivate::tr("Locality");
    case PlaceTown:
        return GeoDataPlacemarkPrivate::tr("Town");
    case PlaceTownCapital:
        return GeoDataPlacemarkPrivate::tr("Town Capital");
    case PlaceTownNationalCapital:
        return GeoDataPlacemarkPrivate::tr("National Capital");
    case PlaceVillage:
        return GeoDataPlacemarkPrivate::tr("Village");
    case PlaceVillageCapital:
        return GeoDataPlacemarkPrivate::tr("Village Capital");
    case PlaceVillageNationalCapital:
        return GeoDataPlacemarkPrivate::tr("National Capital");
    case NaturalWater:
        return GeoDataPlacemarkPrivate::tr("Water");
    case NaturalReef:
        return GeoDataPlacemarkPrivate::tr("Reef");
    case NaturalWood:
        return GeoDataPlacemarkPrivate::tr("Wood");
    case NaturalBeach:
        return GeoDataPlacemarkPrivate::tr("Beach");
    case NaturalWetland:
        return GeoDataPlacemarkPrivate::tr("Wetland");
    case NaturalGlacier:
        return GeoDataPlacemarkPrivate::tr("Glacier");
    case NaturalIceShelf:
        return GeoDataPlacemarkPrivate::tr("Ice Shelf");
    case NaturalScrub:
        return GeoDataPlacemarkPrivate::tr("Scrub");
    case NaturalCliff:
        return GeoDataPlacemarkPrivate::tr("Cliff");
    case NaturalHeath:
        return GeoDataPlacemarkPrivate::tr("Heath");
    case HighwayTrafficSignals:
        return GeoDataPlacemarkPrivate::tr("Traffic Signals");
    case HighwayElevator:
        return GeoDataPlacemarkPrivate::tr("Elevator");
    case HighwaySteps:
        return GeoDataPlacemarkPrivate::tr("Steps");
    case HighwayUnknown:
        return GeoDataPlacemarkPrivate::tr("Unknown Road");
    case HighwayPath:
        return GeoDataPlacemarkPrivate::tr("Path");
    case HighwayFootway:
        return GeoDataPlacemarkPrivate::tr("Footway");
    case HighwayTrack:
        return GeoDataPlacemarkPrivate::tr("Track");
    case HighwayPedestrian:
        return GeoDataPlacemarkPrivate::tr("Footway");
    case HighwayCycleway:
        return GeoDataPlacemarkPrivate::tr("Cycleway");
    case HighwayService:
        return GeoDataPlacemarkPrivate::tr("Service Road");
    case HighwayRoad:
        return GeoDataPlacemarkPrivate::tr("Road");
    case HighwayResidential:
        return GeoDataPlacemarkPrivate::tr("Residential Road");
    case HighwayLivingStreet:
        return GeoDataPlacemarkPrivate::tr("Living Street");
    case HighwayUnclassified:
        return GeoDataPlacemarkPrivate::tr("Unclassified Road");
    case HighwayTertiaryLink:
        return GeoDataPlacemarkPrivate::tr("Tertiary Link Road");
    case HighwayTertiary:
        return GeoDataPlacemarkPrivate::tr("Tertiary Road");
    case HighwaySecondaryLink:
        return GeoDataPlacemarkPrivate::tr("Secondary Link Road");
    case HighwaySecondary:
        return GeoDataPlacemarkPrivate::tr("Secondary Road");
    case HighwayPrimaryLink:
        return GeoDataPlacemarkPrivate::tr("Primary Link Road");
    case HighwayPrimary:
        return GeoDataPlacemarkPrivate::tr("Primary Road");
    case HighwayRaceway:
        return GeoDataPlacemarkPrivate::tr("Raceway");
    case HighwayTrunkLink:
        return GeoDataPlacemarkPrivate::tr("Trunk Link Road");
    case HighwayTrunk:
        return GeoDataPlacemarkPrivate::tr("Trunk Road");
    case HighwayMotorwayLink:
        return GeoDataPlacemarkPrivate::tr("Motorway Link Road");
    case HighwayMotorway:
        return GeoDataPlacemarkPrivate::tr("Motorway");
    case HighwayCorridor:
        return GeoDataPlacemarkPrivate::tr("Corridor");
    case Building:
        return GeoDataPlacemarkPrivate::tr("Building");
    case AccomodationCamping:
        return GeoDataPlacemarkPrivate::tr("Camping");
    case AccomodationHostel:
        return GeoDataPlacemarkPrivate::tr("Hostel");
    case AccomodationHotel:
        return GeoDataPlacemarkPrivate::tr("Hotel");
    case AccomodationMotel:
        return GeoDataPlacemarkPrivate::tr("Motel");
    case AccomodationYouthHostel:
        return GeoDataPlacemarkPrivate::tr("Youth Hostel");
    case AccomodationGuestHouse:
        return GeoDataPlacemarkPrivate::tr("Guest House");
    case AmenityLibrary:
        return GeoDataPlacemarkPrivate::tr("Library");
    case AmenityKindergarten:
        return GeoDataPlacemarkPrivate::tr("Kindergarten");
    case EducationCollege:
        return GeoDataPlacemarkPrivate::tr("College");
    case EducationSchool:
        return GeoDataPlacemarkPrivate::tr("School");
    case EducationUniversity:
        return GeoDataPlacemarkPrivate::tr("University");
    case FoodBar:
        return GeoDataPlacemarkPrivate::tr("Bar");
    case FoodBiergarten:
        return GeoDataPlacemarkPrivate::tr("Biergarten");
    case FoodCafe:
        return GeoDataPlacemarkPrivate::tr("Cafe");
    case FoodFastFood:
        return GeoDataPlacemarkPrivate::tr("Fast Food");
    case FoodPub:
        return GeoDataPlacemarkPrivate::tr("Pub");
    case FoodRestaurant:
        return GeoDataPlacemarkPrivate::tr("Restaurant");
    case HealthDentist:
        return GeoDataPlacemarkPrivate::tr("Dentist");
    case HealthDoctors:
        return GeoDataPlacemarkPrivate::tr("Doctors");
    case HealthHospital:
        return GeoDataPlacemarkPrivate::tr("Hospital");
    case HealthPharmacy:
        return GeoDataPlacemarkPrivate::tr("Pharmacy");
    case HealthVeterinary:
        return GeoDataPlacemarkPrivate::tr("Veterinary");
    case MoneyAtm:
        return GeoDataPlacemarkPrivate::tr("ATM");
    case MoneyBank:
        return GeoDataPlacemarkPrivate::tr("Bank");
    case HistoricArchaeologicalSite:
        return GeoDataPlacemarkPrivate::tr("Archaeological Site");
    case AmenityEmbassy:
        return GeoDataPlacemarkPrivate::tr("Embassy");
    case AmenityEmergencyPhone:
        return GeoDataPlacemarkPrivate::tr("Emergency Phone");
    case AmenityMountainRescue:
        return GeoDataPlacemarkPrivate::tr("Mountain Rescue");
    case LeisureWaterPark:
        return GeoDataPlacemarkPrivate::tr("Water Park");
    case AmenityCommunityCentre:
        return GeoDataPlacemarkPrivate::tr("Community Centre");
    case AmenityFountain:
        return GeoDataPlacemarkPrivate::tr("Fountain");
    case AmenityNightClub:
        return GeoDataPlacemarkPrivate::tr("Night Club");
    case AmenityBench:
        return GeoDataPlacemarkPrivate::tr("Bench");
    case AmenityCourtHouse:
        return GeoDataPlacemarkPrivate::tr("Court House");
    case AmenityFireStation:
        return GeoDataPlacemarkPrivate::tr("Fire Station");
    case AmenityHuntingStand:
        return GeoDataPlacemarkPrivate::tr("Hunting Stand");
    case AmenityPolice:
        return GeoDataPlacemarkPrivate::tr("Police");
    case AmenityPostBox:
        return GeoDataPlacemarkPrivate::tr("Post Box");
    case AmenityPostOffice:
        return GeoDataPlacemarkPrivate::tr("Post Office");
    case AmenityPrison:
        return GeoDataPlacemarkPrivate::tr("Prison");
    case AmenityRecycling:
        return GeoDataPlacemarkPrivate::tr("Recycling");
    case AmenityShelter:
        return GeoDataPlacemarkPrivate::tr("Shelter");
    case AmenityTelephone:
        return GeoDataPlacemarkPrivate::tr("Telephone");
    case AmenityToilets:
        return GeoDataPlacemarkPrivate::tr("Toilets");
    case AmenityTownHall:
        return GeoDataPlacemarkPrivate::tr("Town Hall");
    case AmenityWasteBasket:
        return GeoDataPlacemarkPrivate::tr("Waste Basket");
    case AmenityDrinkingWater:
        return GeoDataPlacemarkPrivate::tr("Drinking Water");
    case AmenityGraveyard:
        return GeoDataPlacemarkPrivate::tr("Graveyard");
    case AmenityChargingStation:
        return GeoDataPlacemarkPrivate::tr("Charging Station");
    case AmenityCarWash:
        return GeoDataPlacemarkPrivate::tr("Car Wash");
    case AmenitySocialFacility:
        return GeoDataPlacemarkPrivate::tr("Social Facility");
    case BarrierCityWall:
        return GeoDataPlacemarkPrivate::tr("City Wall");
    case BarrierGate:
        return GeoDataPlacemarkPrivate::tr("Gate");
    case BarrierLiftGate:
        return GeoDataPlacemarkPrivate::tr("Lift Gate");
    case BarrierWall:
        return GeoDataPlacemarkPrivate::tr("Wall");
    case NaturalVolcano:
        return GeoDataPlacemarkPrivate::tr("Volcano");
    case NaturalPeak:
        return GeoDataPlacemarkPrivate::tr("Peak");
    case NaturalTree:
        return GeoDataPlacemarkPrivate::tr("Tree");
    case NaturalCave:
        return GeoDataPlacemarkPrivate::tr("Cave Entrance");
    case ShopBeverages:
        return GeoDataPlacemarkPrivate::tr("Beverages");
    case ShopHifi:
        return GeoDataPlacemarkPrivate::tr("Hifi");
    case ShopSupermarket:
        return GeoDataPlacemarkPrivate::tr("Supermarket");
    case ShopAlcohol:
        return GeoDataPlacemarkPrivate::tr("Liquor Store");
    case ShopBakery:
        return GeoDataPlacemarkPrivate::tr("Bakery");
    case ShopButcher:
        return GeoDataPlacemarkPrivate::tr("Butcher");
    case ShopConfectionery:
        return GeoDataPlacemarkPrivate::tr("Confectionery");
    case ShopConvenience:
        return GeoDataPlacemarkPrivate::tr("Convenience Shop");
    case ShopGreengrocer:
        return GeoDataPlacemarkPrivate::tr("Greengrocer");
    case ShopSeafood:
        return GeoDataPlacemarkPrivate::tr("Seafood Shop");
    case ShopDepartmentStore:
        return GeoDataPlacemarkPrivate::tr("Department Store");
    case ShopKiosk:
        return GeoDataPlacemarkPrivate::tr("Kiosk");
    case ShopBag:
        return GeoDataPlacemarkPrivate::tr("Bag Shop");
    case ShopClothes:
        return GeoDataPlacemarkPrivate::tr("Clothes Shop");
    case ShopFashion:
        return GeoDataPlacemarkPrivate::tr("Fashion Shop");
    case ShopJewelry:
        return GeoDataPlacemarkPrivate::tr("Jewelry Shop");
    case ShopShoes:
        return GeoDataPlacemarkPrivate::tr("Shoe Shop");
    case ShopVarietyStore:
        return GeoDataPlacemarkPrivate::tr("Variety Store");
    case ShopBeauty:
        return GeoDataPlacemarkPrivate::tr("Beauty Services");
    case ShopChemist:
        return GeoDataPlacemarkPrivate::tr("Chemist");
    case ShopCosmetics:
        return GeoDataPlacemarkPrivate::tr("Cosmetics");
    case ShopHairdresser:
        return GeoDataPlacemarkPrivate::tr("Hairdresser");
    case ShopOptician:
        return GeoDataPlacemarkPrivate::tr("Optician");
    case ShopPerfumery:
        return GeoDataPlacemarkPrivate::tr("Perfumery");
    case ShopDoitYourself:
        return GeoDataPlacemarkPrivate::tr("Hardware Store");
    case ShopFlorist:
        return GeoDataPlacemarkPrivate::tr("Florist");
    case ShopHardware:
        return GeoDataPlacemarkPrivate::tr("Hardware Store");
    case ShopFurniture:
        return GeoDataPlacemarkPrivate::tr("Furniture Store");
    case ShopElectronics:
        return GeoDataPlacemarkPrivate::tr("Electronics Shop");
    case ShopMobilePhone:
        return GeoDataPlacemarkPrivate::tr("Mobile Phone Shop");
    case ShopBicycle:
        return GeoDataPlacemarkPrivate::tr("Bicycle Shop");
    case ShopCar:
        return GeoDataPlacemarkPrivate::tr("Car Dealer");
    case ShopCarRepair:
        return GeoDataPlacemarkPrivate::tr("Car Repair Shop");
    case ShopCarParts:
        return GeoDataPlacemarkPrivate::tr("Car Parts");
    case ShopMotorcycle:
        return GeoDataPlacemarkPrivate::tr("Motorcycle Shop");
    case ShopOutdoor:
        return GeoDataPlacemarkPrivate::tr("Outdoor Shop");
    case ShopSports:
        return GeoDataPlacemarkPrivate::tr("Sports Shop");
    case ShopCopy:
        return GeoDataPlacemarkPrivate::tr("Printing Services");
    case ShopArt:
        return GeoDataPlacemarkPrivate::tr("Art Shop");
    case ShopMusicalInstrument:
        return GeoDataPlacemarkPrivate::tr("Musical Instrument Shop");
    case ShopPhoto:
        return GeoDataPlacemarkPrivate::tr("Photo Shop");
    case ShopBook:
        return GeoDataPlacemarkPrivate::tr("Bookshop");
    case ShopGift:
        return GeoDataPlacemarkPrivate::tr("Gift Shop");
    case ShopStationery:
        return GeoDataPlacemarkPrivate::tr("Stationery");
    case ShopLaundry:
        return GeoDataPlacemarkPrivate::tr("Laundry");
    case ShopPet:
        return GeoDataPlacemarkPrivate::tr("Pet Shop");
    case ShopToys:
        return GeoDataPlacemarkPrivate::tr("Toy Store");
    case ShopTravelAgency:
        return GeoDataPlacemarkPrivate::tr("Travel Agency");
    case ShopDeli:
        return GeoDataPlacemarkPrivate::tr("Deli");
    case ShopTobacco:
        return GeoDataPlacemarkPrivate::tr("Tobacco Shop");
    case ShopTea:
        return GeoDataPlacemarkPrivate::tr("Tea Shop");
    case ShopComputer:
        return GeoDataPlacemarkPrivate::tr("Computer Shop");
    case ShopGardenCentre:
        return GeoDataPlacemarkPrivate::tr("Garden Centre");
    case Shop:
        return GeoDataPlacemarkPrivate::tr("Shop");
    case ManmadeBridge:
        return GeoDataPlacemarkPrivate::tr("Bridge");
    case ManmadeLighthouse:
        return GeoDataPlacemarkPrivate::tr("Lighthouse");
    case ManmadePier:
        return GeoDataPlacemarkPrivate::tr("Pier");
    case ManmadeWaterTower:
        return GeoDataPlacemarkPrivate::tr("Water Tower");
    case ManmadeWindMill:
        return GeoDataPlacemarkPrivate::tr("Wind Mill");
    case ManmadeCommunicationsTower:
        return GeoDataPlacemarkPrivate::tr("Communications Tower");
    case TourismAttraction:
        return GeoDataPlacemarkPrivate::tr("Tourist Attraction");
    case TourismArtwork:
        return GeoDataPlacemarkPrivate::tr("Artwork");
    case HistoricCastle:
        return GeoDataPlacemarkPrivate::tr("Castle");
    case AmenityCinema:
        return GeoDataPlacemarkPrivate::tr("Cinema");
    case TourismInformation:
        return GeoDataPlacemarkPrivate::tr("Information");
    case HistoricMonument:
        return GeoDataPlacemarkPrivate::tr("Monument");
    case TourismMuseum:
        return GeoDataPlacemarkPrivate::tr("Museum");
    case HistoricRuins:
        return GeoDataPlacemarkPrivate::tr("Ruin");
    case AmenityTheatre:
        return GeoDataPlacemarkPrivate::tr("Theatre");
    case TourismThemePark:
        return GeoDataPlacemarkPrivate::tr("Theme Park");
    case TourismViewPoint:
        return GeoDataPlacemarkPrivate::tr("View Point");
    case TourismZoo:
        return GeoDataPlacemarkPrivate::tr("Zoo");
    case TourismAlpineHut:
        return GeoDataPlacemarkPrivate::tr("Alpine Hut");
    case TourismWildernessHut:
        return GeoDataPlacemarkPrivate::tr("Wilderness Hut");
    case HistoricMemorial:
        return GeoDataPlacemarkPrivate::tr("Memorial");
    case TransportAerodrome:
        return GeoDataPlacemarkPrivate::tr("Aerodrome");
    case TransportHelipad:
        return GeoDataPlacemarkPrivate::tr("Helipad");
    case TransportAirportGate:
        return GeoDataPlacemarkPrivate::tr("Airport Gate");
    case TransportAirportRunway:
        return GeoDataPlacemarkPrivate::tr("Airport Runway");
    case TransportAirportApron:
        return GeoDataPlacemarkPrivate::tr("Airport Apron");
    case TransportAirportTaxiway:
        return GeoDataPlacemarkPrivate::tr("Airport Taxiway");
    case TransportAirportTerminal:
        return GeoDataPlacemarkPrivate::tr("Airport Terminal");
    case TransportBusStation:
        return GeoDataPlacemarkPrivate::tr("Bus Station");
    case TransportBusStop:
        return GeoDataPlacemarkPrivate::tr("Bus Stop");
    case TransportCarShare:
        return GeoDataPlacemarkPrivate::tr("Car Sharing");
    case TransportFuel:
        return GeoDataPlacemarkPrivate::tr("Gas Station");
    case TransportParking:
        return GeoDataPlacemarkPrivate::tr("Parking");
    case TransportParkingSpace:
        return GeoDataPlacemarkPrivate::tr("Parking Space");
    case TransportPlatform:
        return GeoDataPlacemarkPrivate::tr("Platform");
    case TransportRentalBicycle:
        return GeoDataPlacemarkPrivate::tr("Bicycle Rental");
    case TransportRentalCar:
        return GeoDataPlacemarkPrivate::tr("Car Rental");
    case TransportRentalSki:
        return GeoDataPlacemarkPrivate::tr("Ski Rental");
    case TransportTaxiRank:
        return GeoDataPlacemarkPrivate::tr("Taxi Rank");
    case TransportTrainStation:
        return GeoDataPlacemarkPrivate::tr("Train Station");
    case TransportTramStop:
        return GeoDataPlacemarkPrivate::tr("Tram Stop");
    case TransportBicycleParking:
        return GeoDataPlacemarkPrivate::tr("Bicycle Parking");
    case TransportMotorcycleParking:
        return GeoDataPlacemarkPrivate::tr("Motorcycle Parking");
    case TransportSubwayEntrance:
        return GeoDataPlacemarkPrivate::tr("Subway Entrance");
    case TransportSpeedCamera:
        return GeoDataPlacemarkPrivate::tr("Speed Camera");
    case ReligionPlaceOfWorship:
        return GeoDataPlacemarkPrivate::tr("Place Of Worship");
    case ReligionBahai:
        return GeoDataPlacemarkPrivate::tr("Bahai");
    case ReligionBuddhist:
        return GeoDataPlacemarkPrivate::tr("Buddhist");
    case ReligionChristian:
        return GeoDataPlacemarkPrivate::tr("Christian");
    case ReligionMuslim:
        return GeoDataPlacemarkPrivate::tr("Muslim");
    case ReligionHindu:
        return GeoDataPlacemarkPrivate::tr("Hindu");
    case ReligionJain:
        return GeoDataPlacemarkPrivate::tr("Jain");
    case ReligionJewish:
        return GeoDataPlacemarkPrivate::tr("Jewish");
    case ReligionShinto:
        return GeoDataPlacemarkPrivate::tr("Shinto");
    case ReligionSikh:
        return GeoDataPlacemarkPrivate::tr("Sikh");
    case ReligionTaoist:
        return GeoDataPlacemarkPrivate::tr("Taoist");
    case LeisureGolfCourse:
        return GeoDataPlacemarkPrivate::tr("Golf Course");
    case LeisureMarina:
        return GeoDataPlacemarkPrivate::tr("Marina");
    case LeisurePark:
        return GeoDataPlacemarkPrivate::tr("Park");
    case LeisurePlayground:
        return GeoDataPlacemarkPrivate::tr("Playground");
    case LeisurePitch:
        return GeoDataPlacemarkPrivate::tr("Pitch");
    case LeisureSportsCentre:
        return GeoDataPlacemarkPrivate::tr("Sports Centre");
    case LeisureStadium:
        return GeoDataPlacemarkPrivate::tr("Stadium");
    case LeisureTrack:
        return GeoDataPlacemarkPrivate::tr("Track");
    case LeisureSwimmingPool:
        return GeoDataPlacemarkPrivate::tr("Swimming Pool");
    case LeisureMinigolfCourse:
        return GeoDataPlacemarkPrivate::tr("Miniature Golf Course");
    case LanduseAllotments:
        return GeoDataPlacemarkPrivate::tr("Allotments");
    case LanduseBasin:
        return GeoDataPlacemarkPrivate::tr("Basin");
    case LanduseCemetery:
        return GeoDataPlacemarkPrivate::tr("Cemetery");
    case LanduseCommercial:
        return GeoDataPlacemarkPrivate::tr("Commercial");
    case LanduseConstruction:
        return GeoDataPlacemarkPrivate::tr("Construction");
    case LanduseFarmland:
        return GeoDataPlacemarkPrivate::tr("Farmland");
    case LanduseFarmyard:
        return GeoDataPlacemarkPrivate::tr("Farmyard");
    case LanduseGarages:
        return GeoDataPlacemarkPrivate::tr("Garages");
    case LanduseGrass:
        return GeoDataPlacemarkPrivate::tr("Grass");
    case LanduseIndustrial:
        return GeoDataPlacemarkPrivate::tr("Industrial");
    case LanduseLandfill:
        return GeoDataPlacemarkPrivate::tr("Landfill");
    case LanduseMeadow:
        return GeoDataPlacemarkPrivate::tr("Meadow");
    case LanduseMilitary:
        return GeoDataPlacemarkPrivate::tr("Military");
    case LanduseQuarry:
        return GeoDataPlacemarkPrivate::tr("Quarry");
    case LanduseRailway:
        return GeoDataPlacemarkPrivate::tr("Railway");
    case LanduseReservoir:
        return GeoDataPlacemarkPrivate::tr("Reservoir");
    case LanduseResidential:
        return GeoDataPlacemarkPrivate::tr("Residential");
    case LanduseRetail:
        return GeoDataPlacemarkPrivate::tr("Retail");
    case LanduseOrchard:
        return GeoDataPlacemarkPrivate::tr("Orchard");
    case LanduseVineyard:
        return GeoDataPlacemarkPrivate::tr("Vineyard");
    case RailwayRail:
        return GeoDataPlacemarkPrivate::tr("Rail");
    case RailwayNarrowGauge:
        return GeoDataPlacemarkPrivate::tr("Narrow Gauge");
    case RailwayTram:
        return GeoDataPlacemarkPrivate::tr("Tram");
    case RailwayLightRail:
        return GeoDataPlacemarkPrivate::tr("Light Rail");
    case RailwayAbandoned:
        return GeoDataPlacemarkPrivate::tr("Abandoned Railway");
    case RailwaySubway:
        return GeoDataPlacemarkPrivate::tr("Subway");
    case RailwayPreserved:
        return GeoDataPlacemarkPrivate::tr("Preserved Railway");
    case RailwayMiniature:
        return GeoDataPlacemarkPrivate::tr("Miniature Railway");
    case RailwayConstruction:
        return GeoDataPlacemarkPrivate::tr("Railway Construction");
    case RailwayMonorail:
        return GeoDataPlacemarkPrivate::tr("Monorail");
    case RailwayFunicular:
        return GeoDataPlacemarkPrivate::tr("Funicular Railway");
    case PowerTower:
        return GeoDataPlacemarkPrivate::tr("Power Tower");
    case AerialwayStation:
        return GeoDataPlacemarkPrivate::tr("Aerialway Station");
    case AerialwayPylon:
        return GeoDataPlacemarkPrivate::tr("Pylon", "A pylon supporting the aerialway cable e.g. on a ski lift");
    case AerialwayCableCar:
        return GeoDataPlacemarkPrivate::tr("Cable Car");
    case AerialwayGondola:
        return GeoDataPlacemarkPrivate::tr("Gondola");
    case AerialwayChairLift:
        return GeoDataPlacemarkPrivate::tr("Chair Lift");
    case AerialwayMixedLift:
        return GeoDataPlacemarkPrivate::tr("Mixed Lift");
    case AerialwayDragLift:
        return GeoDataPlacemarkPrivate::tr("Drag Lift");
    case AerialwayTBar:
        return GeoDataPlacemarkPrivate::tr("T-Bar");
    case AerialwayJBar:
        return GeoDataPlacemarkPrivate::tr("J-Bar");
    case AerialwayPlatter:
        return GeoDataPlacemarkPrivate::tr("Platter");
    case AerialwayRopeTow:
        return GeoDataPlacemarkPrivate::tr("Rope Tow");
    case AerialwayMagicCarpet:
        return GeoDataPlacemarkPrivate::tr("Magic Carpet");
    case AerialwayZipLine:
        return GeoDataPlacemarkPrivate::tr("Zip Line");
    case AerialwayGoods:
        return GeoDataPlacemarkPrivate::tr("Goods");
    case PisteDownhill:
        return GeoDataPlacemarkPrivate::tr("Downhill Piste");
    case PisteNordic:
        return GeoDataPlacemarkPrivate::tr("Nordic Piste");
    case PisteSkitour:
        return GeoDataPlacemarkPrivate::tr("Skitour");
    case PisteSled:
        return GeoDataPlacemarkPrivate::tr("Sled Piste");
    case PisteHike:
        return GeoDataPlacemarkPrivate::tr("Winter Hike");
    case PisteSleigh:
        return GeoDataPlacemarkPrivate::tr("Sleigh Piste");
    case PisteIceSkate:
        return GeoDataPlacemarkPrivate::tr("Ice Skate");
    case PisteSnowPark:
        return GeoDataPlacemarkPrivate::tr("Snow Park");
    case PistePlayground:
        return GeoDataPlacemarkPrivate::tr("Ski Playground");
    case PisteSkiJump:
        return GeoDataPlacemarkPrivate::tr("Ski Jump");
    case AdminLevel1:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 1)");
    case AdminLevel2:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 2)");
    case AdminLevel3:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 3)");
    case AdminLevel4:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 4)");
    case AdminLevel5:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 5)");
    case AdminLevel6:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 6)");
    case AdminLevel7:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 7)");
    case AdminLevel8:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 8)");
    case AdminLevel9:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 9)");
    case AdminLevel10:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 10)");
    case AdminLevel11:
        return GeoDataPlacemarkPrivate::tr("Admin Boundary (Level 11)");
    case BoundaryMaritime:
        return GeoDataPlacemarkPrivate::tr("Boundary (Maritime)");
    case Landmass:
        return GeoDataPlacemarkPrivate::tr("Land Mass");
    case UrbanArea:
        return GeoDataPlacemarkPrivate::tr("Urban Area");
    case InternationalDateLine:
        return GeoDataPlacemarkPrivate::tr("International Date Line");
    case Bathymetry:
        return GeoDataPlacemarkPrivate::tr("Bathymetry");
    case WaterwayCanal:
        return GeoDataPlacemarkPrivate::tr("Canal");
    case WaterwayDrain:
        return GeoDataPlacemarkPrivate::tr("Drain");
    case WaterwayDitch:
        return GeoDataPlacemarkPrivate::tr("Ditch");
    case WaterwayStream:
        return GeoDataPlacemarkPrivate::tr("Stream");
    case WaterwayRiver:
        return GeoDataPlacemarkPrivate::tr("River");
    case WaterwayWeir:
        return GeoDataPlacemarkPrivate::tr("Weir");
    case CrossingSignals:
        return GeoDataPlacemarkPrivate::tr("Crosswalk");
    case CrossingIsland:
        return GeoDataPlacemarkPrivate::tr("Crosswalk");
    case CrossingZebra:
        return GeoDataPlacemarkPrivate::tr("Crosswalk");
    case CrossingRailway:
        return GeoDataPlacemarkPrivate::tr("Railway Crossing");
    case IndoorDoor:
        return GeoDataPlacemarkPrivate::tr("Door");
    case IndoorWall:
        return GeoDataPlacemarkPrivate::tr("Wall");
    case IndoorRoom:
        return GeoDataPlacemarkPrivate::tr("Room");

    case Default:
    case Unknown:
    case None:
    case LastIndex:
        return {};
    }

    return {};
}

qreal GeoDataPlacemark::area() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_placemarkExtendedData ? d->m_placemarkExtendedData->m_area : -1.0;
}

void GeoDataPlacemark::setArea(qreal area)
{
    if (area == -1.0 && !d_func()->m_placemarkExtendedData) {
        return; // nothing to do
    }

    Q_D(GeoDataPlacemark);
    d->placemarkExtendedData().m_area = area;
}

qint64 GeoDataPlacemark::population() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_population;
}

void GeoDataPlacemark::setPopulation(qint64 population)
{
    Q_D(GeoDataPlacemark);
    d->m_population = population;
}

const QString GeoDataPlacemark::state() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_placemarkExtendedData ? d->m_placemarkExtendedData->m_state : QString();
}

void GeoDataPlacemark::setState(const QString &state)
{
    if (state.isEmpty() && !d_func()->m_placemarkExtendedData) {
        return; // nothing to do
    }

    Q_D(GeoDataPlacemark);
    d->placemarkExtendedData().m_state = state;
}

const QString GeoDataPlacemark::countryCode() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_placemarkExtendedData ? d->m_placemarkExtendedData->m_countrycode : QString();
}

void GeoDataPlacemark::setCountryCode(const QString &countrycode)
{
    if (countrycode.isEmpty() && !d_func()->m_placemarkExtendedData) {
        return; // nothing to do
    }

    Q_D(GeoDataPlacemark);
    d->placemarkExtendedData().m_countrycode = countrycode;
}

bool GeoDataPlacemark::isBalloonVisible() const
{
    Q_D(const GeoDataPlacemark);
    return d->m_placemarkExtendedData ? d->m_placemarkExtendedData->m_isBalloonVisible : false;
}

void GeoDataPlacemark::setBalloonVisible(bool visible)
{
    if (!visible && !d_func()->m_placemarkExtendedData) {
        return; // nothing to do
    }

    Q_D(GeoDataPlacemark);
    d->placemarkExtendedData().m_isBalloonVisible = visible;
}

void GeoDataPlacemark::pack(QDataStream &stream) const
{
    Q_D(const GeoDataPlacemark);
    GeoDataFeature::pack(stream);

    stream << d->placemarkExtendedData().m_countrycode;
    stream << d->placemarkExtendedData().m_area;
    stream << d->m_population;
    if (d->m_geometry) {
        stream << d->m_geometry->geometryId();
        d->m_geometry->pack(stream);
    } else {
        stream << InvalidGeometryId;
    }
}

QXmlStreamWriter &GeoDataPlacemark::pack(QXmlStreamWriter &stream) const
{
    stream.writeStartElement("placemark");

    stream.writeEndElement();
    return stream;
}

QXmlStreamWriter &GeoDataPlacemark::operator<<(QXmlStreamWriter &stream) const
{
    pack(stream);
    return stream;
}

void GeoDataPlacemark::unpack(QDataStream &stream)
{
    Q_D(GeoDataPlacemark);
    GeoDataFeature::unpack(stream);

    stream >> d->placemarkExtendedData().m_countrycode;
    stream >> d->placemarkExtendedData().m_area;
    stream >> d->m_population;
    int geometryId;
    stream >> geometryId;
    GeoDataGeometry *geometry = nullptr;
    switch (geometryId) {
    case InvalidGeometryId:
        break;
    case GeoDataPointId: {
        auto point = new GeoDataPoint;
        point->unpack(stream);
        geometry = point;
    } break;
    case GeoDataLineStringId: {
        auto lineString = new GeoDataLineString;
        lineString->unpack(stream);
        geometry = lineString;
    } break;
    case GeoDataLinearRingId: {
        auto linearRing = new GeoDataLinearRing;
        linearRing->unpack(stream);
        geometry = linearRing;
    } break;
    case GeoDataPolygonId: {
        auto polygon = new GeoDataPolygon;
        polygon->unpack(stream);
        geometry = polygon;
    } break;
    case GeoDataMultiGeometryId: {
        auto multiGeometry = new GeoDataMultiGeometry;
        multiGeometry->unpack(stream);
        geometry = multiGeometry;
    } break;
    case GeoDataModelId:
        break;
    default:
        break;
    };
    if (geometry) {
        delete d->m_geometry;
        d->m_geometry = geometry;
        d->m_geometry->setParent(this);
    }
}

}
