/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/legal
**
** This file is part of the Qt Mobility Components.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3.0 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU General Public License version 3.0 requirements will be
** met: http://www.gnu.org/copyleft/gpl.html.
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qlandmarkfilehandler_lmx_p.h"

#include <qlandmarkmanagerengine.h>
#include <qlandmarkcategory.h>
#include <qlandmarkcategoryid.h>
#include <qgeocoordinate.h>
#include <qgeoaddress.h>

#include <QFile>
#include <QStringList>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
#include <qnumeric.h>
#include <QSqlDatabase>
#include <QSqlError>

#include <QDebug>

QTM_USE_NAMESPACE

bool isURIAbsolute(const QString &uri)
{
    QRegExp regExp("^.+:.+$");
    int result = regExp.indexIn(uri);
    if (result != -1 || uri.isEmpty())
        return true;
    else
        return false;
}

QLandmarkFileHandlerLmx::QLandmarkFileHandlerLmx(const volatile bool  * cancel)
    : QObject(),
    m_reader(0),
    m_writer(0),
    m_option(QLandmarkManager::IncludeCategoryData),
    m_cancel(cancel),
    m_categoryIdNameHash(QHash<QString,QString>())
{
}

QLandmarkFileHandlerLmx::~QLandmarkFileHandlerLmx()
{
    if (m_reader != 0)
        delete m_reader;
    if (m_writer != 0)
        delete m_writer;
}

QList<QLandmark> QLandmarkFileHandlerLmx::landmarks() const
{
    return m_landmarks;
}

void QLandmarkFileHandlerLmx::setLandmarks(const QList<QLandmark> &landmarks)
{
    m_landmarks = landmarks;
}

void QLandmarkFileHandlerLmx::setCategoryIdNameHash(const QHash<QString,QString> &categoryIdNameHash)
{
    m_categoryIdNameHash = categoryIdNameHash;
}

QList<QStringList> QLandmarkFileHandlerLmx::landmarkCategoryNames()
{
    return m_landmarkCategoryNames;
}

void QLandmarkFileHandlerLmx::setTransferOption(QLandmarkManager::TransferOption option) {
    m_option = option;
}

bool QLandmarkFileHandlerLmx::importData(QIODevice *device)
{
    if (m_reader)
        delete m_reader;

    m_reader = new QXmlStreamReader(device);

    if (!readLmx()) {
        if (m_errorCode != QLandmarkManager::CancelError) {//if it wasn't canceled
            m_errorCode = QLandmarkManager::ParsingError;//must've been a parsing error.
            m_error = m_reader->errorString();
        }
        return false;
    } else {
        if (m_reader->atEnd()) {
            m_reader->readNextStartElement();
            if (!m_reader->name().isEmpty()) {
                m_errorCode = QLandmarkManager::ParsingError;
                m_error = QString("A single root element named \"lmx\" was expected (second root element was named \"%1\").").arg(m_reader->name().toString());
                return false;
            }
        }
    }

    m_error = "";
    m_errorCode = QLandmarkManager::NoError;
    return true;
}

bool QLandmarkFileHandlerLmx::readLmx()
{
    /*
    <xsd:element name="lmx">
        <xsd:complexType>
            <xsd:choice>
                <xsd:element name="landmark" type="landmarkType" />
                <xsd:element name="landmarkCollection" type="landmarkCollectionType" />
            </xsd:choice>
        </xsd:complexType>
    </xsd:element>
    */

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("Expected a root element named \"lmx\" (no root element found).");
        return false;
    }

    if (m_reader->name() != "lmx") {
        m_reader->raiseError(QString("The root element is expected to have the name \"lmx\" (root element was named \"%1\").").arg(m_reader->name().toString()));
        return false;
    }

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"lmx\" expected a single child element named either \"landmark\" or \"landmarkCollection\" (no child element found).");
        return false;
    }

    if (m_reader->name() == "landmark") {
        QLandmark lm;
        if (!readLandmark(lm))
            return false;
        m_landmarks << lm;
    } else if (m_reader->name() == "landmarkCollection") {
        if (!readLandmarkCollection(m_landmarks))
            return false;
    } else {
        m_reader->raiseError(QString("The element \"lmx\" expected a single child element named either \"landmark\" or \"landmarkCollection\" (child element was named \"%1\").").arg(m_reader->name().toString()));
        return false;
    }

    if (!m_reader->readNextStartElement()) {
        m_reader->skipCurrentElement();
        return true;
    } else {
        m_reader->raiseError(QString("The element \"lmx\" expected a single child element (second child element was named \"%1\").").arg(m_reader->name().toString()));
        return false;
    }
}

bool QLandmarkFileHandlerLmx::readLandmarkCollection(QList<QLandmark> &landmarkCollection)
{
    /*
    <xsd:complexType name="landmarkCollectionType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string" minOccurs="0" />
            <xsd:element name="description" type="xsd:string" minOccurs="0" />
            <xsd:element name="landmark" type="landmarkType" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    */

    Q_ASSERT(m_reader->isStartElement()
             && (m_reader->name() == "landmarkCollection"));

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"landmarkCollection\" expected 1 or more child elements named \"landmark\" (0 found).");
        return false;
    }

    if (m_reader->name() == "name") {
        // Not used outside of schema compliance check
        m_reader->skipCurrentElement();
        if (!m_reader->readNextStartElement()) {
            m_reader->raiseError("The element \"landmarkCollection\" expected 1 or more child elements named \"landmark\" (0 found).");
            return false;
        }
    }

    if (m_reader->name() == "description") {
        // Not used outside of schema compliance check
        m_reader->skipCurrentElement();
        if (!m_reader->readNextStartElement()) {
            m_reader->raiseError("The element \"landmarkCollection\" expected 1 or more child elements named \"landmark\" (0 found).");
            return false;
        }
    }

    while (m_reader->name() == "landmark") {
        QLandmark lm;
        if (!readLandmark(lm))
            return false;
        landmarkCollection << lm;

        if (!m_reader->readNextStartElement())
            return true;
    }

    m_reader->raiseError(QString("The element \"landmarkCollection\" did not expect a child element named \"%1\" at this point (unknown child element or child element out of order).").arg(m_reader->name().toString()));
    return false;
}

bool QLandmarkFileHandlerLmx::readLandmark(QLandmark &landmark)
{
    /*
    <xsd:complexType name="landmarkType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string" minOccurs="0" />
            <xsd:element name="description" type="xsd:string" minOccurs="0" />
            <xsd:element name="coordinates" type="coordinatesType" minOccurs="0" />
            <xsd:element name="coverageRadius" minOccurs="0">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:float">
                        <xsd:minInclusive value="0"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="addressInfo" type="addressInfoType" minOccurs="0" />
            <xsd:element name="mediaLink" type="mediaLinkType" minOccurs="0" maxOccurs="unbounded" />
            <xsd:element name="category" type="categoryType" minOccurs="0" maxOccurs="unbounded" />
        </xsd:sequence>
    </xsd:complexType>
    */

    if(m_cancel && (*m_cancel) == true) {
        m_errorCode = QLandmarkManager::CancelError;
        m_error = "Import of lmx file was canceled";
        return false;
    }

    Q_ASSERT(m_reader->isStartElement() &&
             (m_reader->name() == "landmark"));

    m_landmarkCategoryNames.append(QStringList());

    if (!m_reader->readNextStartElement())
        return true;

    if (m_reader->name() == "name") {
        landmark.setName(m_reader->readElementText());
        if (!m_reader->readNextStartElement())
            return true;
    }

    if (m_reader->name() == "description") {
        landmark.setDescription(m_reader->readElementText());
        if (!m_reader->readNextStartElement())
            return true;
    }

    if (m_reader->name() == "coordinates") {
        if (!readCoordinates(landmark))
            return false;

        if (!m_reader->readNextStartElement())
            return true;
    }

    if (m_reader->name() == "coverageRadius") {
        bool ok = false;
        QString s = m_reader->readElementText();

        if ((s == "INF") || (s == "-INF") || (s == "NaN")) {
            m_reader->raiseError(QString("The element \"coverageRadius\" expected a value convertable to type real (value was \"%1\").").arg(s));
            return false;
        }

        qreal rad = (qreal)(s.toDouble(&ok));

        if (!ok) {
            m_reader->raiseError(QString("The element \"coverageRadius\" expected a value convertable to type real (value was \"%1\").").arg(s));
            return false;
        }

        if (rad < 0.0) {
            m_reader->raiseError(QString("The element \"coverageRadius\" is expected to have a non-negative value (value was \"%1\").").arg(s));
            return false;
        }

        landmark.setRadius(rad);

        if (!m_reader->readNextStartElement())
            return true;
    }

    if (m_reader->name() == "addressInfo") {
        if (!readAddressInfo(landmark))
            return false;

        if (!m_reader->readNextStartElement())
            return true;
    }

    // TODO need to document the fact that only the first link is read
    // and the others are ignored
    bool mediaLinkRead = false;

    while (m_reader->name() == "mediaLink") {
        if (!mediaLinkRead) {
            mediaLinkRead = true;
            if (!readMediaLink(landmark))
                return false;
        }

        if (!m_reader->readNextStartElement())
            return true;
    }


    QStringList categoryNames;
    while (m_reader->name() == "category") {
        QString name;
        if (!readCategory(name))
            return false;
        categoryNames << name;

        if (!m_reader->readNextStartElement()) {
            m_landmarkCategoryNames.last() = categoryNames;
            return true;
        }
    }

    m_reader->raiseError(QString("The element \"landmark\" did not expect a child element named \"%1\" at this point (unknown child element or child element out of order).").arg(m_reader->name().toString()));
    return false;
}

bool QLandmarkFileHandlerLmx::readCoordinates(QLandmark &landmark)
{
    /*
    <xsd:complexType name="coordinatesType">
        <xsd:sequence>
            <xsd:element name="latitude">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:double">
                        <xsd:minInclusive value="-90"/>
                        <xsd:maxInclusive value="90"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="longitude">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:double">
                        <xsd:minInclusive value="-180"/>
                        <xsd:maxExclusive value="180"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="altitude" type="xsd:float" minOccurs="0" />
            <xsd:element name="horizontalAccuracy" minOccurs="0">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:float">
                        <xsd:minInclusive value="0"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="verticalAccuracy" minOccurs="0">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:float">
                        <xsd:minInclusive value="0"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="timeStamp" type="xsd:dateTime" minOccurs="0" />
        </xsd:sequence>
    </xsd:complexType>
    */

    Q_ASSERT(m_reader->isStartElement() &&
             (m_reader->name() == "coordinates"));

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"coordinates\" did not have the required child element \"latitude\".");
        return false;
    }

    if (m_reader->name() == "latitude") {
        bool ok = false;
        QString s = m_reader->readElementText();

        if ((s == "INF") || (s == "-INF") || (s == "NaN")) {
            m_reader->raiseError(QString("The element \"latitude\" expected a value convertable to type double (value was \"%1\").").arg(s));
            return false;
        }

        double lat = s.toDouble(&ok);

        if (!ok) {
            m_reader->raiseError(QString("The element \"latitude\" expected a value convertable to type double (value was \"%1\").").arg(s));
            return false;
        }

        if (lat < -90.0 || 90.0 < lat) {
            m_reader->raiseError(QString("The element \"latitude\" fell outside of the bounds -90.0 <= latitude <= 90.0 (value was \"%1\").").arg(s));
            return false;
        }

        QGeoCoordinate coord = landmark.coordinate();
        coord.setLatitude(lat);
        landmark.setCoordinate(coord);

        if (!m_reader->readNextStartElement()) {
            m_reader->raiseError("The element \"coordinates\" did not have the required child element \"longitude\".");
            return false;
        }
    } else {
        m_reader->raiseError("The element \"coordinates\" did not have the required child element \"latitude\".");
        return false;
    }

    if (m_reader->name() == "longitude") {
        bool ok = false;
        QString s = m_reader->readElementText();

        if ((s == "INF") || (s == "-INF") || (s == "NaN")) {
            m_reader->raiseError(QString("The element \"longitude\" expected a value convertable to type double (value was \"%1\").").arg(s));
            return false;
        }

        double lon = s.toDouble(&ok);

        if (!ok) {
            m_reader->raiseError(QString("The element \"longitude\" expected a value convertable to type double (value was \"%1\").").arg(s));
            return false;
        }

        if (lon < -180.0 || 180.0 <= lon) {
            m_reader->raiseError(QString("The element \"longitude\" fell outside of the bounds -180.0 <= longitude < 180.0 (value was \"%1\").").arg(s));
            return false;
        }

        QGeoCoordinate coord = landmark.coordinate();
        coord.setLongitude(lon);
        landmark.setCoordinate(coord);

        if (!m_reader->readNextStartElement())
            return true;
    } else {
        m_reader->raiseError("The element \"coordinates\" did not have the required child element \"longitude\".");
        return false;
    }

    if (m_reader->name() == "altitude") {
        bool ok = false;
        QString s = m_reader->readElementText();

        if ((s == "INF") || (s == "-INF") || (s == "NaN")) {
            m_reader->raiseError(QString("The element \"altitude\" expected a value convertable to type double (value was \"%1\").").arg(s));
            return false;
        }

        double alt = s.toDouble(&ok);

        if (!ok) {
            m_reader->raiseError(QString("The element \"altitude\" expected a value convertable to type float (value was \"%1\").").arg(s));
            return false;
        }

        QGeoCoordinate coord = landmark.coordinate();
        coord.setAltitude(alt);
        landmark.setCoordinate(coord);

        if (!m_reader->readNextStartElement())
            return true;
    }

    QList<QString> names;
    names << "horizontalAccuracy";
    names << "verticalAccuracy";
    names << "timeStamp";

    for (int i = 0; i < names.size(); ++i) {
        // Not used outside of schema compliance check
        if (m_reader->name() == names.at(i)) {
           m_reader->skipCurrentElement();
           if (!m_reader->readNextStartElement())
               return true;
        }
    }

    m_reader->raiseError(QString("The element \"coordinate\" did not expect a child element named \"%1\" at this point (unknown child element or child element out of order).").arg(m_reader->name().toString()));
    return false;
}

bool QLandmarkFileHandlerLmx::readAddressInfo(QLandmark &landmark)
{
    /*
    <xsd:complexType name="addressInfoType">
        <xsd:all>
            <xsd:element name="country" type="xsd:string" minOccurs="0" />
            <xsd:element name="countryCode" minOccurs="0">
                <xsd:simpleType>
                    <xsd:restriction base="xsd:token">
                        <xsd:length value="2"/>
                    </xsd:restriction>
                </xsd:simpleType>
            </xsd:element>
            <xsd:element name="state" type="xsd:string" minOccurs="0" />
            <xsd:element name="county" type="xsd:string" minOccurs="0" />
            <xsd:element name="city" type="xsd:string" minOccurs="0" />
            <xsd:element name="district" type="xsd:string" minOccurs="0" />
            <xsd:element name="postalCode" type="xsd:string" minOccurs="0" />
            <xsd:element name="crossing1" type="xsd:string" minOccurs="0" />
            <xsd:element name="crossing2" type="xsd:string" minOccurs="0" />
            <xsd:element name="street" type="xsd:string" minOccurs="0" />
            <xsd:element name="buildingName" type="xsd:string" minOccurs="0" />
            <xsd:element name="buildingZone" type="xsd:string" minOccurs="0" />
            <xsd:element name="buildingFloor" type="xsd:string" minOccurs="0" />
            <xsd:element name="buildingRoom" type="xsd:string" minOccurs="0" />
            <xsd:element name="extension" type="xsd:string" minOccurs="0" />
            <xsd:element name="phoneNumber" type="xsd:string" minOccurs="0" />
        </xsd:all>
    </xsd:complexType>
    */

    Q_ASSERT(m_reader->isStartElement()
             && (m_reader->name() == "addressInfo"));

    QHash<QString, int> counts;
    counts["country"] = 0;
    counts["countryCode"] = 0;
    counts["state"] = 0;
    counts["county"] = 0;
    counts["city"] = 0;
    counts["district"] = 0;
    counts["postalCode"] = 0;
    counts["crossing1"] = 0;
    counts["crossing2"] = 0;
    counts["street"] = 0;
    counts["buildingName"] = 0;
    counts["buildingZone"] = 0;
    counts["buildingFloor"] = 0;
    counts["buildingRoom"] = 0;
    counts["extension"] = 0;
    counts["phoneNumber"] = 0;

    QGeoAddress address;

    while (m_reader->readNextStartElement()) {
        QString name = m_reader->name().toString();
        if (counts.keys().contains(name)) {
            if (counts.value(name) == 1) {
                m_reader->raiseError(QString("The element \"addressInfo\" did not expect more then one occurrence of the child element named \"%1\".").arg(m_reader->name().toString()));
                return false;
            }
            counts[name] = 1;

            if (name == "county") {
                address.setCounty(m_reader->readElementText());
            } else if (name == "country") {
                address.setCountry(m_reader->readElementText());
            } else if (name == "state") {
                address.setState(m_reader->readElementText());
            } else if (name == "city") {
                address.setCity(m_reader->readElementText());
            } else if (name == "district") {
                address.setDistrict(m_reader->readElementText());
            } else if (name == "postalCode") {
                address.setPostcode(m_reader->readElementText());
            } else if (name == "street") {
                QString street = m_reader->readElementText();
                address.setStreet(street);
            } else if (name == "phoneNumber") {
                landmark.setPhoneNumber(m_reader->readElementText());
            } else {
                m_reader->skipCurrentElement();
            }
        } else {
            m_reader->raiseError(QString("The element \"addressInfo\" did not expect a child element named \"%1\".").arg(m_reader->name().toString()));
            return false;
        }
    }

    landmark.setAddress(address);

    return true;
}

bool QLandmarkFileHandlerLmx::readMediaLink(QLandmark &landmark)
{
    /*
    <xsd:complexType name="mediaLinkType">
        <xsd:sequence>
            <xsd:element name="name" type="xsd:string" minOccurs="0" />
            <xsd:element name="mime" type="xsd:string" minOccurs="0" />
            <xsd:element name="url" type="xsd:anyURI" />
        </xsd:sequence>
    </xsd:complexType>
    */

    Q_ASSERT(m_reader->isStartElement()
             && (m_reader->name() == "mediaLink"));

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"mediaLink\" did not have the required child element \"url\".");
        return false;
    }

    if (m_reader->name() == "name") {
        // Not used outside of schema compliance check
        m_reader->skipCurrentElement();
        if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"mediaLink\" did not have the required child element \"url\".");
            return false;
        }
    }

    if (m_reader->name() == "mime") {
        // Not used outside of schema compliance check
        m_reader->skipCurrentElement();
        if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"mediaLink\" did not have the required child element \"url\".");
            return false;
        }
    }

    if (m_reader->name() == "url") {
        landmark.setUrl(m_reader->readElementText());
        if (!m_reader->readNextStartElement())
            return true;
    }

    m_reader->raiseError(QString("The element \"url\" did not expect a child element named \"%1\" at this point (unknown child element or child element out of order).").arg(m_reader->name().toString()));
    return false;
}

bool QLandmarkFileHandlerLmx::readCategory(QString &name)
{
    /*
    <xsd:complexType name="categoryType">
        <xsd:sequence>
            <xsd:element name="id" type="xsd:unsignedShort" minOccurs="0" />
            <xsd:element name="name" type="xsd:string" />
        </xsd:sequence>
    </xsd:complexType>
    */

    Q_ASSERT(m_reader->isStartElement()
             && (m_reader->name() == "category"));

    if (!m_reader->readNextStartElement()) {
        m_reader->raiseError("The element \"category\" did not have the required child element \"name\".");
        return false;
    }

    QString idString;

    if (m_reader->name() == "id") {
        bool ok = false;
        idString = m_reader->readElementText();
        (void)idString.toUShort(&ok);

        if (!ok) {
            m_reader->raiseError(QString("The element \"id\" expected a value convertable to type unsigned short (value was \"%1\").").arg(idString));
            return false;
        }

        if (!m_reader->readNextStartElement()) {
            m_reader->raiseError("The element \"category\" did not have the required child element \"name\".");
            return false;
        }
    }

    if (m_reader->name() == "name") {
        name = m_reader->readElementText();
        if (!m_reader->readNextStartElement()) {
            return true;
        }
    }

    m_reader->raiseError(QString("The element \"category\" did not expect a child element named \"%1\" at this point (unknown child element or child element out of order).").arg(m_reader->name().toString()));
    return false;
}

bool QLandmarkFileHandlerLmx::exportData(QIODevice *device, const QString &nsPrefix)
{
    if (m_writer != 0)
        delete m_writer;

    m_writer = new QXmlStreamWriter(device);
    m_writer->setAutoFormatting(true);

    m_nsPrefix = nsPrefix;

    bool result = writeLmx();

    if(!result) {
        return false;
    }

    m_error = "";
    emit finishedExport();
    return true;
}

bool QLandmarkFileHandlerLmx::writeLmx()
{
    // LMX files must contain at least one landmark
    if (m_landmarks.size() == 0)
        return true;

    QString nsLmx = "http://www.nokia.com/schemas/location/landmarks/1/0/";
    QString nsXsi = "http://www.w3.org/2001/XMLSchema-instance";

    if (!m_nsPrefix.isEmpty())
        m_ns = nsLmx;
    else
        m_ns = "";

    m_writer->writeStartDocument();

    if (m_nsPrefix.isEmpty())
        m_writer->writeDefaultNamespace(nsLmx);
    else {
        m_writer->writeNamespace(nsLmx, m_nsPrefix);
    }

    m_writer->writeStartElement(m_ns, "lmx");
    m_writer->writeNamespace(nsXsi, "xsi");
    m_writer->writeAttribute(nsXsi, "schemaLocation", "http://www.nokia.com/schemas/location/landmarks/1/0/ lmx.xsd");

    if (m_landmarks.size() > 1) {
        if(!writeLandmarkCollection(m_landmarks))
            return false;
    } else {
        if(!writeLandmark(m_landmarks.at(0)))
            return false;
    }

    m_writer->writeEndElement();
    m_writer->writeEndDocument();

    return true;
}

bool QLandmarkFileHandlerLmx::writeLandmarkCollection(const QList<QLandmark> &landmarkCollection)
{
    Q_UNUSED(landmarkCollection);
    m_writer->writeStartElement(m_ns, "landmarkCollection");

    for (int i = 0; i < m_landmarks.size(); ++i) {
        if(m_cancel && (*m_cancel) == true) {
            m_errorCode = QLandmarkManager::CancelError;
            m_error = "Export of lmx file was canceled";
            return false;
        }

        if (!writeLandmark(m_landmarks.at(i)))
            return false;
    }

    m_writer->writeEndElement();

    return true;
}

bool QLandmarkFileHandlerLmx::writeLandmark(const QLandmark &landmark)
{
    m_writer->writeStartElement(m_ns, "landmark");

    if (!landmark.name().isEmpty())
        m_writer->writeTextElement(m_ns, "name", landmark.name());

    if (!landmark.description().isEmpty())
        m_writer->writeTextElement(m_ns, "description", landmark.description());

    if (landmark.coordinate().isValid())
        if (!writeCoordinates(landmark))
            return false;

    if (landmark.radius() > 0)
        m_writer->writeTextElement(m_ns, "coverageRadius", QString::number(landmark.radius()));

    if (!writeAddressInfo(landmark))
        return false;

    if (!landmark.url().isEmpty())
        if (!writeMediaLink(landmark))
            return false;

    if (m_option != QLandmarkManager::ExcludeCategoryData) {
        for (int i = 0; i < landmark.categoryIds().size(); ++i) {
            if (!writeCategory(landmark.categoryIds().at(i)))
                return false;
        }
    }

    m_writer->writeEndElement();

    return true;
}

bool QLandmarkFileHandlerLmx::writeCoordinates(const QLandmark &landmark)
{
    m_writer->writeStartElement(m_ns, "coordinates");

    double lat = landmark.coordinate().latitude();
    double lon = landmark.coordinate().longitude();
    double alt = landmark.coordinate().altitude();

    if (qIsNaN(lat))
        m_writer->writeTextElement(m_ns, "latitude", "NaN");
    else
        m_writer->writeTextElement(m_ns, "latitude", QString::number(lat));

    if (qIsNaN(lon))
        m_writer->writeTextElement(m_ns, "longitude", "NaN");
    else
        m_writer->writeTextElement(m_ns, "longitude", QString::number(lon));

    if (!qIsNaN(alt))
        m_writer->writeTextElement(m_ns, "altitude", QString::number(alt));

    m_writer->writeEndElement();

    return true;
}

bool QLandmarkFileHandlerLmx::writeAddressInfo(const QLandmark &landmark)
{
    QGeoAddress address = landmark.address();

    if (address.street().isEmpty()
            && address.city().isEmpty()
            && address.state().isEmpty()
            && address.country().isEmpty()
            && address.postcode().isEmpty()
            && landmark.phoneNumber().isEmpty())
        return true;

    m_writer->writeStartElement(m_ns, "addressInfo");

    if (!address.country().isEmpty())
        m_writer->writeTextElement(m_ns, "country", address.country());

    if (!address.state().isEmpty())
        m_writer->writeTextElement(m_ns, "state", address.state());

    if (!address.county().isEmpty())
        m_writer->writeTextElement(m_ns, "county", address.county());

    if (!address.city().isEmpty())
        m_writer->writeTextElement(m_ns, "city", address.city());

    if (!address.district().isEmpty())
        m_writer->writeTextElement(m_ns, "district", address.district());

    if (!address.postcode().isEmpty())
        m_writer->writeTextElement(m_ns, "postalCode", address.postcode());

    QString street;

    if (!address.street().isEmpty())
        street.append(address.street());

    if (!street.isEmpty())
        m_writer->writeTextElement(m_ns, "street", street);

    if (!landmark.phoneNumber().isEmpty())
        m_writer->writeTextElement(m_ns, "phoneNumber", landmark.phoneNumber());

    m_writer->writeEndElement();

    return true;
}

bool QLandmarkFileHandlerLmx::writeMediaLink(const QLandmark &landmark)
{
    //only write out URIs which are absolute in accordance the specification
    //for lmx 1.0
    if (isURIAbsolute(landmark.url().toString())) {
        m_writer->writeStartElement(m_ns, "mediaLink");
        m_writer->writeTextElement(m_ns, "url", landmark.url().toString());
        m_writer->writeEndElement();
    }

    return true;
}

bool QLandmarkFileHandlerLmx::writeCategory(const QLandmarkCategoryId &id)
{
    if (!id.isValid()) {
        m_errorCode = QLandmarkManager::BadArgumentError;
        m_error = QString("The category with id \"%1\" from manager \"%2\" is invalid.").arg(id.localId()).arg(id.managerUri());
        return false;
    }

    if (!m_categoryIdNameHash.contains(id.localId())) {
        m_errorCode = QLandmarkManager::UnknownError;
        m_error = "Category for landmark could not be identified";
        return false;
    }

    m_writer->writeStartElement(m_ns, "category");
    m_writer->writeTextElement(m_ns, "name", m_categoryIdNameHash.value(id.localId()));
    m_writer->writeEndElement();

    return true;
}

QString QLandmarkFileHandlerLmx::errorString() const
{
    return m_error;
}

QLandmarkManager::Error QLandmarkFileHandlerLmx::errorCode() const
{
    return m_errorCode;
}

#include "moc_qlandmarkfilehandler_lmx_p.cpp"
