/***************************** LICENSE START ***********************************

 Copyright 2012 ECMWF and INPE. This software is distributed under the terms
 of the Apache License version 2.0. In applying this license, ECMWF does not
 waive the privileges and immunities granted to it by virtue of its status as
 an Intergovernmental Organization or submit itself to any jurisdiction.

 ***************************** LICENSE END *************************************/

#include <QApplication>
#include <QBuffer>
#include <QDateTime>
#include <QDebug>
#include <QDomDocument>
#include <QFile>
#include <QImage>
#include <QUrl>

#include <QAuthenticator>
#include <QNetworkReply>
#include <QDomDocument>

#include "MvQNetworkAccessManager.h"
#include "MvQOgcRequest.h"

#include "MvQWmsClient.h"

#include "LogHandler.h"
#include "StatusMsgHandler.h"

#include "mars.h"
#include "Metview.h"

class DomFinder
{
public:
    static QDomElement findElement(const QDomElement& elem, QString path);
    static QDomElement findElementByCondition(const QDomElement& elem, const QString& tag, const QString& key, const QString& value);
    static QList<QDomElement> findDirectChildren(const QDomElement& elem, const QString& tag);
    static void attributes(const QDomElement& elem, QMap<QString, QString>& att);

private:
    static QDomElement find(const QDomElement& elem, QStringList pathLst);
};


QDomElement DomFinder::findElement(const QDomElement& elem, QString path)
{
    auto pathLst = path.split("/");
    if (!pathLst.isEmpty()) {
        if (elem.nodeName() == pathLst[0]) {
            return find(elem, pathLst);
        }
        else {
            auto s = elem.namedItem(pathLst[0]);
            if (s.isElement()) {
                return find(s.toElement(), pathLst);
            }
        }
    }
    return {};
}

QDomElement DomFinder::find(const QDomElement& elem, QStringList pathLst)
{
    if (!elem.isNull()) {
        if (elem.nodeName() == pathLst[0]) {
            pathLst.removeFirst();
            if (pathLst.isEmpty()) {
                return elem;
            }
            else {
                QDomNode n = elem.firstChild();
                while (!n.isNull()) {
                    auto v = find(n.toElement(), pathLst);
                    if (!v.isNull()) {
                        return v;
                    }
                    n = n.nextSibling();
                }
            }
        }
    }

    return {};
}


QDomElement DomFinder::findElementByCondition(const QDomElement& elem, const QString& tag, const QString& key, const QString& value)
{
    if (elem.isElement() && elem.tagName() == tag) {
        auto v = elem.namedItem(key).toElement().text().simplified();
        if (v == value) {
            return elem;
        }

        QDomNode n = elem.firstChild();
        while (!n.isNull()) {
            if (n.isElement() && n.toElement().tagName() == tag) {
                auto r = findElementByCondition(n.toElement(), tag, key, value);
                if (!r.isNull()) {
                    return r;
                }
            }
            n = n.nextSibling();
        }
    }

    return {};
}

QList<QDomElement> DomFinder::findDirectChildren(const QDomElement& elem, const QString& tag)
{
    QList<QDomElement> lst;
    QDomNode n = elem.firstChild();
    while (!n.isNull()) {
        if (n.isElement() && n.toElement().tagName() == tag) {
            lst << n.toElement();
        }
        n = n.nextSibling();
    }
    return lst;
}

void DomFinder::attributes(const QDomElement& elem, QMap<QString, QString>& att)
{
    auto a = elem.attributes();
    for (int i = 0; i < a.count(); i++) {
        auto item = a.item(i);
        att[item.toAttr().name()] = item.toAttr().value().simplified();
    }
}


//==============================
//
// MvQProjInfo
//
//==============================

MvQProjInfo::MvQProjInfo(const MvRequest& inReq)
{
    if (strcmp(inReq.getVerb(), "GEOVIEW") != 0)
        return;

    const char* info = inReq("_ZOOM_DEFINITION");
    if (info) {
        parseProj4String(info);
    }
    else {
        parseRequest(inReq);
    }
}


void MvQProjInfo::parseProj4String(const char* info)
{
    if (!info)
        return;

    QString str(info);
    str.remove("\"");
    str.remove("{");
    str.remove("}");

    foreach (QString s, str.split(",")) {
        QStringList s1 = s.split(":");
        if (s1.count() == 2) {
            insert(s1[0].simplified(), s1[1].simplified());
        }
    }

    if (value("subpage_map_projection") == "cylindrical") {
        if (value("subpage_map_area_definition") == "full") {
            insert("subpage_lower_left_latitude", "-90");
            insert("subpage_lower_left_longitude", "-180");
            insert("subpage_upper_right_latitude", "90");
            insert("subpage_upper_right_longitude", "180");
        }
    }
}

void MvQProjInfo::parseRequest(const MvRequest& inReq)
{
    MvRequest ctx;

    // Expand the request
    char* shareDir = getenv("METVIEW_DIR_SHARE");
    if (shareDir) {
        std::string path(shareDir);
        path.append("/etc/");

        std::string defFileName = path + "GeoViewDef";
        std::string rulesFileName = path + "GeoViewRules";

        MvLanguage langMetview(defFileName.c_str(),
                               rulesFileName.c_str(),
                               EXPAND_DEFAULTS);

        ctx = langMetview.expandOne(inReq);
    }
    else {
        ctx = inReq;
    }

    const char* proj = (const char*)ctx("MAP_PROJECTION");

    if (proj && strcmp(proj, "CYLINDRICAL") == 0) {
        insert("subpage_map_projection", "cylindrical");

        const char* ad = (const char*)ctx("MAP_AREA_DEFINITION");
        QString areaDef;
        if (ad) {
            areaDef = QString(ad);
            areaDef = areaDef.toLower();
            insert("subpage_map_area_definition", areaDef);
        }
        else
            return;

        if (areaDef == "corners" && ctx.countValues("AREA") == 4) {
            double dval = 0.;
            ctx.getValue(dval, "AREA", 0);
            insert("subpage_lower_left_latitude", QString::number(dval));
            ctx.getValue(dval, "AREA", 1);
            insert("subpage_lower_left_longitude", QString::number(dval));
            ctx.getValue(dval, "AREA", 2);
            insert("subpage_upper_right_latitude", QString::number(dval));
            ctx.getValue(dval, "AREA", 3);
            insert("subpage_upper_right_longitude", QString::number(dval));
        }

        else if (areaDef == "full") {
            insert("subpage_lower_left_latitude", "-90");
            insert("subpage_lower_left_longitude", "-180");
            insert("subpage_upper_right_latitude", "90");
            insert("subpage_upper_right_longitude", "180");
        }
    }
}

QString MvQProjInfo::get(QString key)
{
    QMap<QString, QString>::const_iterator it = find(key);
    if (it != end())
        return it.value();

    else
        return {};
}


//==============================
//
// Message handler for XQuery
//
//==============================

#if 0
void MvQWmsMessageHandler::handleMessage(QtMsgType type, const QString& description, const QUrl& /*identifier*/,
                                         const QSourceLocation& /*sourceLocation*/)
{
    if (type == QtFatalMsg) {
        fatalMsg_ << description;
    }
}
#endif

//==============================
//
// Base client
//
//==============================

MvQWmsClientBase::MvQWmsClientBase()
{
    supportedMimeType_ << "image/png"
                       << "image/jpeg"
                       << "image/gif";  // "image/svg+xml";

    supportedCrs_ << "EPSG:4326"
                  << "CRS:84";

    //    queryMsgHandler_ = new MvQWmsMessageHandler;
}

MvQWmsClientBase::~MvQWmsClientBase() = default;

QString MvQWmsClientBase::getMimeType(QByteArray ba)
{
    if (ba.mid(0, 8).toHex() == "89504e470d0a1a0a") {
        return "image/png";
    }
    else if (ba.mid(0, 4).toHex() == "ffd8ffe0") {
        return "image/jpeg";
    }
    else if (ba.mid(0, 6) == "GIF87a" ||
             ba.mid(0, 6) == "GIF89a") {
        return "image/gif";
    }

    return {"text/plain"};
}


QString MvQWmsClientBase::getRequestedMimeType(QString r)
{
    MvQGetMapRequest req;
    req.setRequest(r);
    return req.item("FORMAT");
}

//==============================
//
// Download/batch client
//
//==============================

MvQWmsGetClient::MvQWmsGetClient(MvRequest& req) :
    reqInfo_(req)
{
    // Mode
    const char* modec = (const char*)reqInfo_("_ACTION");
    QString mode;
    if (modec)
        mode = QString(modec);

    // Nosubsets
    const char* noSubsets = (const char*)reqInfo_("LAYER_NOSUBSETS");

    // Time dimensions
    QStringList timeDimName;
    const char* tdn = (const char*)reqInfo_("TIME_DIMENSIONS");
    if (tdn) {
        QString s(tdn);
        timeDimName = s.split("/");
    }

    // Create and initialise request object
    getMapRequest_ = new MvQGetMapRequest;
    getMapRequest_->setRequest(QString((const char*)reqInfo_("REQUEST")), timeDimName);

    // Update bbox if needed
    if ((mode == "prepare" || mode == "update") &&
        (noSubsets == nullptr || strcmp(noSubsets, "0") == 0)) {
        requestChanged_ = updateBoundingBox();
    }

    // In update mode if bbox was not updated we quit
    if (mode == "update" && requestChanged_ == false) {
        return;
    }

    // Legend and log path
    const char* legend = (const char*)reqInfo_("LAYER_LEGEND");
    if (legend)
        legendPath_ = QString(legend);

    const char* logo = (const char*)reqInfo_("LAYER_LOGO");
    if (logo)
        logoPath_ = QString(logo);


    // Network access
    networkGetMap_ = new MvQNetworkAccessManager(this);
    networkLegend_ = new MvQNetworkAccessManager(this);
    networkLogo_ = new MvQNetworkAccessManager(this);

    connect(networkGetMap_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyGetMap(QNetworkReply*)));

    connect(networkLegend_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyLegend(QNetworkReply*)));

    connect(networkLogo_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyLogo(QNetworkReply*)));
}

MvQWmsGetClient::~MvQWmsGetClient()
{
    delete getMapRequest_;
    if (networkGetMap_)
        delete networkGetMap_;
    if (networkLegend_)
        delete networkLegend_;
    if (networkLogo_)
        delete networkLogo_;
}

bool MvQWmsGetClient::updateBoundingBox()
{
    // Get current view area in the plot

    MvRequest ctxIn = reqInfo_("_CONTEXT");
    if (!ctxIn || strcmp(ctxIn.getVerb(), "GEOVIEW") != 0)
        return false;

    // ctxIn.print();
    MvQProjInfo projInfo(ctxIn);
    // ctxIn.print();

    QList<float> areaL;

    // Cylindrical
    if (projInfo.get("subpage_map_projection") == "cylindrical") {
        if (projInfo.get("subpage_map_area_definition") == "corners" ||
            projInfo.get("subpage_map_area_definition") == "full") {
            areaL << projInfo.get("subpage_lower_left_latitude").toDouble();
            areaL << projInfo.get("subpage_lower_left_longitude").toDouble();
            areaL << projInfo.get("subpage_upper_right_latitude").toDouble();
            areaL << projInfo.get("subpage_upper_right_longitude").toDouble();
        }
    }


#if 0
	MvRequest ctx;
	MvRequest ctxIn=reqInfo_("_CONTEXT");
	
	ctxIn.print();
	
	char *shareDir=getenv("METVIEW_DIR_SHARE");
	if(shareDir)
	{	
		string path(shareDir);
		path.append("/etc/");

		string defFileName=path+ "GeoViewDef";
		string rulesFileName=path + "GeoViewRules";

		MvLanguage langMetview ( defFileName.c_str(),
				  rulesFileName.c_str(),
				  EXPAND_DEFAULTS);

	        ctx=langMetview.expandOne (ctxIn);
        }
        else
	{
	  	ctx=ctxIn;
	}	

	ctx.print();

	const char* proj= (const char*)ctx("MAP_PROJECTION");
	
	int cnt = ctx.countValues("AREA");
	double dval; 
	QList<float> areaL;
	for(int i=0; i< cnt; i++)
	{
		ctx.getValue(dval,"AREA",i);
	 	areaL << dval;
	}

	//Check if we have the view area definition
	if(!proj || areaL.count() != 4)
	{
		return false;
	}
#endif
    // Max bounding box for the given CRS/SRS
    const char* bbChar = (const char*)reqInfo_("MAX_BOUNDING_BOX");
    QRectF maxBox;
    if (bbChar) {
        QString s(bbChar);
        QStringList lst = s.split("/");
        if (lst.count() == 4) {
            maxBox = QRectF(lst[0].toFloat(), lst[1].toFloat(),
                            lst[2].toFloat() - lst[0].toFloat(),
                            lst[3].toFloat() - lst[1].toFloat());
        }
    }

    if (maxBox.isEmpty()) {
        return false;
    }

    // Current bounding box from the request
    QRectF currentBox;
    QStringList lst = getMapRequest_->item("BBOX").split(",");
    if (lst.count() == 4) {
        currentBox = QRectF(lst[0].toFloat(), lst[1].toFloat(),
                            lst[2].toFloat() - lst[0].toFloat(),
                            lst[3].toFloat() - lst[1].toFloat());
    }

    if (currentBox.isEmpty()) {
        return false;
    }

    // We reached this point only if both view area and
    // bounding box were found!!!!

    if (projInfo.get("subpage_map_projection") == "cylindrical") {
        // areaL: s,w,n,e
        // bbox: mix, miny, max, maxy
        // qDebug() << "area" << areaL;

        QRectF areaBox;

        // For 1.3.0 in epsg:4326 x is lat and y is lon !!!!!
        if (getMapRequest_->request().contains("CRS=", Qt::CaseInsensitive) &&
            getMapRequest_->crs() == "EPSG:4326") {
            areaBox = QRectF(areaL[0], areaL[1], areaL[2] - areaL[0], areaL[3] - areaL[1]);
        }
        else if (getMapRequest_->crs() == "EPSG:4326" || getMapRequest_->crs() == "CRS:84") {
            areaBox = QRectF(areaL[1], areaL[0], areaL[3] - areaL[1], areaL[2] - areaL[0]);
        }

        if (areaBox.isEmpty()) {
            return false;
        }

        if (areaBox.intersects(maxBox)) {
            // maxAreaBox = the intersection of the viewable area
            // and the max bounding box for the srs/c
            QRectF maxAreaBox = areaBox.intersected(maxBox);

            // Find out the relation of the maxAreaBox and the current bounding
            // box in the request
            QRectF resBox;
            if (maxAreaBox != currentBox) {
                resBox = maxAreaBox;
            }

            if (!resBox.isEmpty()) {
                getMapRequest_->setItem("BBOX",
                                        QString::number(resBox.x()) + "," +
                                            QString::number(resBox.y()) + "," +
                                            QString::number(resBox.x() + resBox.width()) + "," +
                                            QString::number(resBox.y() + resBox.height()));

                return true;
            }
        }
    }

    return false;
}

void MvQWmsGetClient::runGetMap()
{
    outFiles_.clear();

    QStringList lst = getMapRequest_->requestFrame();
    getMapFrameNum_ = lst.count();
    getMapFrameToDownload_ = 0;

    getMapRunStatus_ = false;
    downloadGetMapFrame();
}

void MvQWmsGetClient::downloadGetMapFrame()
{
    // qDebug() << "request" << getMapFrameToDownload_ << getMapRequest_->requestFrame(getMapFrameToDownload_);

    QNetworkRequest netReq(QUrl::fromPercentEncoding(getMapRequest_->requestFrame(getMapFrameToDownload_).toUtf8()));
    // netReq.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());

    getMapReply_ = networkGetMap_->get(netReq);
}

void MvQWmsGetClient::downloadLegend()
{
    if (!legendPath_.isEmpty()) {
        // qDebug() << "legend" << QUrl::fromPercentEncoding(legendPath_.toAscii());

        QNetworkRequest netReq(QUrl::fromPercentEncoding(legendPath_.toUtf8()));
        // netReq.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());
        legendReply_ = networkLegend_->get(netReq);
    }
    else {
        // QApplication::exit();
        downloadLogo();
    }
}

void MvQWmsGetClient::downloadLogo()
{
    if (!logoPath_.isEmpty()) {
        logoReply_ = networkLogo_->get(QNetworkRequest(QUrl::fromPercentEncoding(logoPath_.toUtf8())));
    }
    else {
        QApplication::exit();
    }
}


void MvQWmsGetClient::slotReplyGetMap(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QUrl url = r.url();

    // qDebug() << url.toString();

    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    if (reply->error() != QNetworkReply::NoError) {
        getMapFrameNum_ = 0;
        getMapFrameToDownload_ = -1;

        QString ems = "GetMap request failed!\nError: ";
        ems += reply->errorString();
        marslog(LOG_EROR, "GetMap request failed!\nError: %s ", reply->errorString().toStdString().c_str());
        QApplication::exit();
        return;
    }
    else {
        // temporary file
        QString outFile;
        outFile = marstmp();

        getMapRequest_->setOutFile(getMapFrameToDownload_, outFile);

        QByteArray ba = reply->readAll();

        if (mimeType.isEmpty())
            mimeType = getMimeType(ba);

        // If exception is returned or mime type is
        // different to what was requested
        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml") ||
            !mimeType.contains(getRequestedMimeType(url.toString()))) {
            // readException(ba,eMsg);

            getMapFrameNum_ = 0;
            getMapFrameToDownload_ = -1;

            // marslog(LOG_EROR,"GetMap request failed!\nError: %s ",eMsg.toStdString().c_str());
            marslog(LOG_EROR, "GetMap request failed! ");
            QApplication::exit();
            return;
        }

        QFile out(outFile);
        out.open(QIODevice::WriteOnly);
        out.write(ba);
        out.close();

        getMapFrameToDownload_++;

        if (getMapFrameToDownload_ < getMapFrameNum_) {
            downloadGetMapFrame();
        }
        else {
            getMapFrameNum_ = 0;
            getMapFrameToDownload_ = -1;

            getMapRunStatus_ = true;
            downloadLegend();
            // QApplication::exit();
        }
    }

    reply->deleteLater();
    getMapReply_ = nullptr;
}

void MvQWmsGetClient::slotReplyLegend(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    if (reply->error() != QNetworkReply::NoError) {
        QString ems = "Legend download failed!\nError: ";
        ems += reply->errorString();
        marslog(LOG_EROR, "Legend download failed!\nError: %s ", reply->errorString().toStdString().c_str());
        // log->error(reply->errorString().toStdString() + "\n");
        downloadLogo();
        // QApplication::exit();
        return;
    }
    else {
        QByteArray ba = reply->readAll();

        // If exception is returned ot mime type is
        // different ot what was requested
        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml") ||
            mimeType.contains("text/plain"))  //||
        //   mimeType != getRequestedMimeType(url.toString()))
        {
            QApplication::exit();
            return;
        }
        else {
            QString outFile = QString::fromStdString(marstmp());

            QFile out(outFile);
            out.open(QIODevice::WriteOnly);
            out.write(ba);
            out.close();

            legendImagePath_ = outFile;

            // QApplication::exit();
            downloadLogo();
        }
    }

    reply->deleteLater();
    legendReply_ = nullptr;
}


void MvQWmsGetClient::slotReplyLogo(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    if (reply->error() != QNetworkReply::NoError) {
        QString ems = "Logo download failed!\nError: ";
        ems += reply->errorString();
        marslog(LOG_EROR, "Logo download failed!\nError: %s ", reply->errorString().toStdString().c_str());
        // log->error(reply->errorString().toStdString() + "\n");
        QApplication::exit();
        return;
    }
    else {
        QByteArray ba = reply->readAll();

        // If exception is returned ot mime type is
        // different ot what was requested
        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml") ||
            mimeType.contains("text/plain"))  //||
        //   mimeType != getRequestedMimeType(url.toString()))
        {
            QApplication::exit();
            return;
        }
        else {
            QString outFile = QString::fromStdString(marstmp());

            QFile out(outFile);
            out.open(QIODevice::WriteOnly);
            out.write(ba);
            out.close();

            logoImagePath_ = outFile;

            QImage img(outFile);
            if (img.width() > 100) {
                img.scaledToWidth(100, Qt::SmoothTransformation).save(outFile, "png");
            }

            QApplication::exit();
        }
    }

    reply->deleteLater();
    logoReply_ = nullptr;
}


//=================================
//
// Client for the user interface
//
//=================================

MvQWmsUiClient::MvQWmsUiClient(std::string& iconFile, MvRequest& req) :
    iconFile_(QString::fromStdString(iconFile)),
    reqInfo_(req)
{
    char* shareDir = getenv("METVIEW_DIR_SHARE");
    if (shareDir) {
        std::string path(shareDir);
        path.append("/etc/");

        std::string defFileName = path + "WmsClientDef";
        std::string rulesFileName = path + "WmsClientRules";

        MvLanguage langMetview(defFileName.c_str(),
                               rulesFileName.c_str(),
                               EXPAND_DEFAULTS);

        reqInfo_ = langMetview.expandOne(req);
    }


    reqInfo_.print();

    getMapRequest_ = new MvQGetMapRequest;
    lastGetMapRequest_ = new MvQGetMapRequest;

    // Initialize elements from Metview request
    url_ = QString((const char*)reqInfo_("SERVER"));
    version_ = (const char*)reqInfo_("VERSION");

    QStringList timeDimName;

    const char* tdn = (const char*)reqInfo_("TIME_DIMENSIONS");
    if (tdn) {
        QString s(tdn);
        timeDimName = s.split("/");
    }

    getMapRequest_->setRequest(QString((const char*)reqInfo_("REQUEST")), timeDimName);

    clientMode_ = AdvancedMode;
    if ((const char*)reqInfo_("MODE") != nullptr) {
        QString m(reqInfo_("MODE"));
        if (m == "EXPERT")
            clientMode_ = BasicMode;
        else
            clientMode_ = AdvancedMode;
    }

    if ((const char*)reqInfo_("EXTRA_GETCAP_PAR") != nullptr) {
        extraGetCapPar_ = QString(reqInfo_("EXTRA_GETCAP_PAR"));
    }

    if ((const char*)reqInfo_("EXTRA_GETMAP_PAR") != nullptr) {
        extraGetMapPar_ = QString(reqInfo_("EXTRA_GETMAP_PAR"));
    }

    if ((const char*)reqInfo_("HTTP_USER") != nullptr) {
        httpUser_ = QString(reqInfo_("HTTP_USER"));
    }

    if ((const char*)reqInfo_("HTTP_PASSWORD") != nullptr) {
        httpPassword_ = QString(reqInfo_("HTTP_PASSWORD"));
    }


    // qDebug() << "Init:" << url_ << version_ << getMapRequest_;

    // Width
    width_ = new MvQOgcIntValue(1024, "WIDTH", "Width:");
    QString val = getMapRequest_->item(width_->requestName());
    if (!val.isEmpty())
        width_->setValue(val.toInt());

    // Height
    height_ = new MvQOgcIntValue(512, "HEIGHT", "Height:");
    val = getMapRequest_->item(height_->requestName());
    if (!val.isEmpty())
        height_->setValue(val.toInt());

    // Format
    format_ = new MvQOgcList("FORMAT", "Format:");

    // Transparent
    transparent_ = new MvQOgcList("TRANSPARENT", "Transparent:");
    transparent_->setValues(QStringList() << "TRUE"
                                          << "FALSE");
    transparent_->setDisplayValues(QStringList() << "True"
                                                 << "False");
    val = getMapRequest_->item(transparent_->requestName());

    // If it is a new request we set transparency to TRUE by default
    if (val.isEmpty() && getMapRequest_->request().isEmpty()) {
        transparent_->setCurrentValue("TRUE");
    }
    else {
        transparent_->setCurrentValue(val);
    }

    // BgColour
    bgColour_ = new MvQOgcColourValue("#FFFFFF", "BGCOLOUR", "Bg colour:");

    // Layer
    layer_ = new MvQOgcTree("LAYERS", "Layer:");

    // CRS/SRS
    crs_ = new MvQOgcCrsList("CRS", "CRS:");

    // Style
    style_ = new MvQOgcStyleList("STYLES", "Style:");

    // BoundingBox
    requestBbox_ = new MvQOgcBoundingBox("BBOX", "");
    geographicBbox_ = nullptr;

    // Dimension

    // Network access
    networkGetCap_ = new MvQNetworkAccessManager(this);
    networkPreview_ = new MvQNetworkAccessManager(this);
    networkLegend_ = new MvQNetworkAccessManager(this);
    networkLogo_ = new MvQNetworkAccessManager(this);

    getCapReply_ = nullptr;
    previewReply_ = nullptr;
    legendReply_ = nullptr;
    logoReply_ = nullptr;

    connect(networkGetCap_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyGetCap(QNetworkReply*)));

    connect(networkPreview_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyPreview(QNetworkReply*)));

    connect(networkLegend_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyLegend(QNetworkReply*)));

    connect(networkLogo_, SIGNAL(replyReadyToProcess(QNetworkReply*)),
            this, SLOT(slotReplyLogo(QNetworkReply*)));

    connect(networkGetCap_, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
            this, SLOT(slotAuthentication(QNetworkReply*, QAuthenticator*)));

    connect(networkPreview_, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
            this, SLOT(slotAuthentication(QNetworkReply*, QAuthenticator*)));

    connect(networkLegend_, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
            this, SLOT(slotAuthentication(QNetworkReply*, QAuthenticator*)));

    connect(networkLogo_, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
            this, SLOT(slotAuthentication(QNetworkReply*, QAuthenticator*)));

    previewFile_ = marstmp();
    legendFile_ = marstmp();
    logoFile_ = marstmp();

    // xml namspace
    xlinkNsDeclaration_ = "declare namespace xlink = \"http://www.w3.org/1999/xlink\";";
}

MvQWmsUiClient::~MvQWmsUiClient()
{
    delete width_;
    delete height_;
    delete format_;
    delete transparent_;
    delete bgColour_;
    delete layer_;
    delete crs_;
    delete style_;
    delete requestBbox_;
    delete networkGetCap_;
    delete networkPreview_;
    delete networkLegend_;
    delete networkLogo_;

    clearDimension();
    clearBoundingBox();

    clearPreview();
    clearLegend();
}

void MvQWmsUiClient::reset(QString r)
{
    slotAbortDownloadProcess();

    if (clientMode_ == BasicMode) {
        getMapRequest_->setRequest(r);
    }
    else {
        clearServerDependentData();

        getMapRequest_->setRequest(r);

        QString v = getMapRequest_->item("VERSION");
        if (v.isEmpty()) {
            v = getMapRequest_->item("version");
        }
        url_ = getMapRequest_->guessUrl();
        version_ = v;
    }

    emit clientResetDone();
}


void MvQWmsUiClient::setClientMode(ClientMode mode)
{
    if (clientMode_ == mode)
        return;

    if (mode == AdvancedMode) {
        slotAbortDownloadProcess();

        extraGetMapPar_ = getMapRequest_->item("mv_key_extrapar");

        QString v = getMapRequest_->item("VERSION");
        if (v.isEmpty()) {
            v = getMapRequest_->item("version");
        }

        // qDebug() << "new url:" << getMapRequest_->guessUrl();

        if (url_ != getMapRequest_->guessUrl() ||
            version_ != v) {
            if (v.isEmpty())
                setUrl(getMapRequest_->guessUrl());
            else
                setUrl(getMapRequest_->guessUrl(), v);
        }
    }

    clientMode_ = mode;
}

void MvQWmsUiClient::clearServerDependentData(bool keepRequest)
{
    capability_.clear();
    if (!keepRequest) {
        getMapRequest_->clear();
    }
    format_->clear();
    layer_->clear();
    clearLayerDependentData();
    serviceMeta_.clear();
}

void MvQWmsUiClient::clearLayerDependentData()
{
    crs_->clear();
    style_->clear();
    clearDimension();
    clearBoundingBox();
}

void MvQWmsUiClient::clearDimension()
{
    foreach (MvQOgcDimension* item, dimension_) {
        delete item;
    }
    dimension_.clear();
}

void MvQWmsUiClient::clearBoundingBox()
{
    foreach (MvQOgcBoundingBox* item, bbox_) {
        delete item;
    }
    bbox_.clear();

    if (geographicBbox_) {
        delete geographicBbox_;
        geographicBbox_ = nullptr;
    }
}

void MvQWmsUiClient::setUrl(const QString& url, const QString version)
{
    if (url_ != url) {
        slotAbortDownloadProcess();
        url_ = url;
        version_ = version;
        clearServerDependentData();
    }
}

void MvQWmsUiClient::setVersion(const QString& version)
{
    if (version_ != version) {
        slotAbortDownloadProcess();
        version_ = version;
        // clearServerDependentData();
    }
}

void MvQWmsUiClient::slotRunGetCapabilities()
{
    lastGetMapRequest_->setRequest(getMapRequest_->request());
    clearServerDependentData(true);  // keeps the original request

    QString r = url_;

    r += "?SERVICE=WMS&";
    if (version_ != "Default") {
        r += "VERSION=" + version_ + "&";
    }
    r += "REQUEST=GetCapabilities";
    if (!extraGetCapPar_.isEmpty()) {
        r += "&" + extraGetCapPar_;
    }

    GuiLog().task() << "Load GetCapabalitites"
                    << " ..in progress";

    StatusMsgHandler::instance()->show("Load GetCapabilitites ...", true);

    QUrl url = QUrl::fromPercentEncoding(r.toUtf8());
    QNetworkRequest nr(url);
    // nr.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());

    getCapReply_ = networkGetCap_->get(QNetworkRequest(nr));
}


void MvQWmsUiClient::downloadPreview(QSize previewSize)
{
    MvQGetMapRequest req;
    req.setRequest(getMapRequest_->requestFrame(0));

    previewSize_ = previewSize;

    QString w = QString::number(previewSize_.width());
    QString h = QString::number(previewSize_.height());

    MvQOgcNode* node = layer_->currentNode();
    if (node) {
        if (!node->attribute("fixedWidth").isEmpty() &&
            node->attribute("fixedWidth").toInt() > 0) {
            w = node->attribute("fixedWidth");
        }
        if (!node->attribute("fixedHeight").isEmpty() &&
            node->attribute("fixedHeight").toInt() > 0) {
            h = node->attribute("fixedHeight");
        }
    }

    req.setItem("WIDTH", w);
    req.setItem("HEIGHT", h);

    GuiLog().task() << "Load preview" << GuiLog().requestKey() << req.request().toStdString();
    // log->setStatus("In progress");
    StatusMsgHandler::instance()->show(tr("Load preview ..."), true);

    QNetworkRequest netReq(QUrl::fromPercentEncoding(req.request().toUtf8()));
    netReq.setOriginatingObject(this);
    // netReq.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());


    previewReply_ = networkPreview_->get(netReq);
}

void MvQWmsUiClient::downloadLegend()
{
    if (!style_->currentLegend().isEmpty()) {
        GuiLog().task() << "Load legend" << GuiLog().requestKey() << style_->currentLegend().toStdString();
        // log->setStatus("In progress");

        StatusMsgHandler::instance()->show(tr("Load legend"), true);

        // Fix for KNMI radar
        QNetworkRequest netReq(QUrl::fromPercentEncoding(style_->currentLegend().toUtf8()));
        netReq.setOriginatingObject(this);
        // netReq.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());
        legendReply_ = networkLegend_->get(netReq);
    }
    else {
        emit legendLoaded(QString());
    }
}

void MvQWmsUiClient::downloadLogo()
{
    MvQOgcNode* node = layer_->currentNode();
    if (node && !node->logo().isEmpty()) {
        GuiLog().task() << "Load logo" << GuiLog().requestKey() << node->logo().toStdString();
        // log->setStatus("In progress");
        StatusMsgHandler::instance()->show(tr("Load logo"), true);

        // Fix for KNMI radar
        QNetworkRequest netReq(QUrl::fromPercentEncoding(node->logo().toUtf8()));
        netReq.setOriginatingObject(this);
        // netReq.setRawHeader("Authorization", "Basic " + QByteArray(QString("%1:%2").arg("egows").arg("egows").toAscii()).toBase64());
        logoReply_ = networkLogo_->get(netReq);
    }
    else {
        emit logoLoaded(QString());
    }
}

void MvQWmsUiClient::setGetMapRequest(QString r)
{
    if (clientMode_ == BasicMode) {
        getMapRequest_->editRequest(r);
        emit getMapRequestChanged(getMapRequest_->request(), getMapRequest_->requestFrame());
    }
}


void MvQWmsUiClient::slotSaveInfo()
{
    // Get info
    MvRequest r("WMSCLIENT");

    r("SERVER") = url_.toStdString().c_str();
    r("VERSION") = version_.toStdString().c_str();
    r("REQUEST") = getMapRequest_->request().toStdString().c_str();
    r("TIME_DIMENSIONS") = timeDimName().join("/").toStdString().c_str();
    // r("NON_TIME_DIMENSIONS") =  nonTimeDimName().join("/").toStdString().c_str();

    //	reqInfo_("SERVICE") = 1;
    if (clientMode_ == BasicMode) {
        r("MODE") = "EXPERT";
    }
    else {
        r("MODE") = "INTERACTIVE";
    }

    r("EXTRA_GETCAP_PAR") = extraGetCapPar_.toStdString().c_str();
    r("EXTRA_GETMAP_PAR") = extraGetMapPar_.toStdString().c_str();

    r("HTTP_USER") = httpUser_.toStdString().c_str();
    r("HTTP_PASSWORD") = httpPassword_.toStdString().c_str();

    r("LAYER_TITLE") = layer_->currentDisplayValue().toStdString().c_str();
    r("LAYER_DESCRIPTION") = layer_->currentDescription().toStdString().c_str();
    r("SERVICE_TITLE") = serviceMeta_["Title"].toStdString().c_str();
    r("LAYER_LEGEND") = style_->currentLegend().toStdString().c_str();

    MvQOgcNode* node = layer_->currentNode();
    if (node) {
        r("LAYER_LOGO") = node->logo().toStdString().c_str();

        if (!node->attribute("noSubsets").isEmpty()) {
            r("LAYER_NOSUBSETS") = node->attribute("noSubsets").toStdString().c_str();
        }
        if (!node->attribute("fixedWidth").isEmpty()) {
            r("LAYER_FIXEDWITH") = node->attribute("fixedWidth").toStdString().c_str();
        }
        if (!node->attribute("fixedHeight").isEmpty()) {
            r("LAYER_FIXEDHEIGHT") = node->attribute("fixedHeight").toStdString().c_str();
        }
    }

    r("MAX_BOUNDING_BOX") = getMapRequest_->item("BBOX").split(",").join("/").toStdString().c_str();

    // qDebug() << version_;
    // qDebug() << url_;

    MvRequest rout;

    r.print();

    char* shareDir = getenv("METVIEW_DIR_SHARE");
    if (shareDir) {
        std::string path(shareDir);
        path.append("/etc/");

        std::string defFileName = path + "WmsClientDef";
        std::string rulesFileName = path + "WmsClientRules";

        MvLanguage langMetview(defFileName.c_str(),
                               rulesFileName.c_str(),
                               EXPAND_NO_DEFAULT);

        rout = langMetview.expandOne(r);

        if (!rout)
            rout = r;
    }
    else {
        rout = r;
    }

    rout.print();

    // Save info request
    rout.save(iconFile_.toStdString().c_str());
}


void MvQWmsUiClient::slotAuthentication(QNetworkReply* /*reply*/, QAuthenticator* auth)
{
    auth->setUser("egows");
    auth->setPassword("egows");

    // auth->setUser(httpUser_);
    // auth->setPassword(httpPassword_);
}

void MvQWmsUiClient::slotReplyGetCap(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    GuiLog().task() << "Load GetCapabilities";

    if (reply->error() != QNetworkReply::NoError) {
        GuiLog().error() << reply->errorString().toStdString();
        StatusMsgHandler::instance()->failed();

        emit getCapabilityLoaded();
        // QApplication::exit();
    }
    else {
        QByteArray ba = reply->readAll();

        QString eMsg;
        if (!mimeType.contains("application/vnd.ogc.wms_xml"))  // ||
                                                                //   mimeType.contains("text/xml"))
                                                                //&& mimeType == "application/vnd.ogc.se_xml")
        {
            readException(ba, eMsg);
        }

        if (!eMsg.isEmpty()) {
            capability_.clear();
            GuiLog().error() << eMsg.toStdString();
            emit getCapabilityLoaded();
            StatusMsgHandler::instance()->failed();
        }
        else {
            capability_ = QString(ba);
            dom_ = QDomDocument();
            dom_.setContent(capability_);

            // Uncomment these lines to load a getCapabilities file from the disk
            /*QFile file("/var/tmp/dummy-user/PERFORCE/metview_4/src/OgcClient/marc_dla.xml");
                if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
                    return;

            capability_=QString(file.readAll());*/

            repliedVersion_ = readVersion();
            readService();
            readFormat();
            readLayer();

            updateAfterGetCapLoaded();

            emit getCapabilityLoaded();
            StatusMsgHandler::instance()->done();
        }
    }

    reply->deleteLater();
    getCapReply_ = nullptr;
}

void MvQWmsUiClient::updateAfterGetCapLoaded()
{
    QString val;

    if (lastGetMapRequest_->request().isEmpty()) {
        return;
    }

    val = lastGetMapRequest_->item(width_->requestName());
    width_->setValue(val.toInt());

    val = lastGetMapRequest_->item(height_->requestName());
    height_->setValue(val.toInt());

    val = lastGetMapRequest_->item(transparent_->requestName());
    transparent_->setCurrentValue(val);

    val = lastGetMapRequest_->item(format_->requestName());
    format_->setCurrentValue(val);

    val = lastGetMapRequest_->item(layer_->requestName());
    layer_->setCurrentValue(val);

    updateAfterLayerLoaded();
}

void MvQWmsUiClient::updateAfterLayerLoaded()
{
    // lastGetMapRequest_->setRequest(getMapRequest_->request());

    QString val;

    MvQOgcNode* node = layer_->currentNode();
    if (node) {
        if (!node->attribute("fixedWidth").isEmpty()) {
            val = node->attribute("fixedWidth");
            if (val.toInt() > 0)
                width_->setValue(val.toInt());
        }
        if (!node->attribute("fixedHeight").isEmpty()) {
            val = node->attribute("fixedHeight");
            if (val.toInt() > 0)
                height_->setValue(val.toInt());
        }
    }

    // readLayerAbstract();
    readLayerProperties();
    //    readStyle();
    //    readBoundingBox();  //bounding box should be called before crs!!!!!
    //    readCrs();
    //    readGeographicBoundingBox();
    //    readDimension();
    // readAttribution();

    val = lastGetMapRequest_->item(style_->requestName());
    style_->setCurrentValue(val);

    val = lastGetMapRequest_->item(crs_->requestName());
    crs_->setCurrentValue(val);

    updatRequestBoundingBox();

    foreach (MvQOgcDimension* dim, dimension_) {
        val = lastGetMapRequest_->item(dim->requestName());
        dim->setCurrentValues(val);
    }

    buildGetMapRequest();

    // qDebug() << lastGetMapRequest_->request() << "\n" << getMapRequest_->request();
}

void MvQWmsUiClient::slotReplyPreview(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QUrl url = r.url();

    // qDebug() << url.toString();

    GuiLog().task() << "Load preview";

    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    if (reply->error() != QNetworkReply::NoError) {
        GuiLog().error() << reply->errorString().toStdString();
        emit previewLoaded(QString());
        StatusMsgHandler::instance()->failed();
    }
    else {
        QString outFile = previewFile_;
        QByteArray ba = reply->readAll();

        if (mimeType.isEmpty())
            mimeType = getMimeType(ba);

        // qDebug() << "preview mimeType" << mimeType;

        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml") ||
            !mimeType.contains(getRequestedMimeType(url.toString()))) {
            QString eMsg;
            readException(ba, eMsg);

            GuiLog().error() << eMsg.toStdString();
            emit previewLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else if (mimeType.contains("text/plain")) {
            QString eMsg(ba);

            GuiLog().error() << eMsg.toStdString();
            emit previewLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else {
            QImage img = QImage::fromData(ba);
            if (!img.isNull()) {
                img = img.scaledToWidth(previewSize_.width(), Qt::SmoothTransformation);
                img.save(outFile, "png");
            }
            else {
                QFile out(outFile);
                out.open(QIODevice::WriteOnly);
                out.write(ba);
                out.close();
            }

            emit previewLoaded(outFile);
            StatusMsgHandler::instance()->done();
        }
    }

    reply->deleteLater();
    previewReply_ = nullptr;
}

void MvQWmsUiClient::slotReplyLegend(QNetworkReply* reply)
{
    // Here, reply is not necessary the legendReply_ because the
    // request might have been redirected!!
    QNetworkRequest r = reply->request();
    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    GuiLog().task() << "Load legend";

    if (reply->error() != QNetworkReply::NoError) {
        GuiLog().error() << reply->errorString().toStdString();
        emit legendLoaded(QString());
        StatusMsgHandler::instance()->failed();
    }
    else {
        QString outFile = legendFile_;
        QByteArray ba = reply->readAll();

        if (mimeType.isEmpty())
            mimeType = getMimeType(ba);

        // qDebug() << "mime" << mimeType << getRequestedMimeType(url.toString());

        // If exception is returned or mime type is
        // different to what was requested
        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml"))
        //    || mimeType != getRequestedMimeType(url.toString()))
        {
            QString eMsg;
            readException(ba, eMsg);

            GuiLog().error() << eMsg.toStdString();
            emit legendLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else if (mimeType.contains("text/plain")) {
            QString eMsg(ba);

            GuiLog().error() << eMsg.toStdString();
            emit legendLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else {
            // qDebug() << outFile;

            QFile out(outFile);
            out.open(QIODevice::WriteOnly);
            out.write(ba);
            out.close();

            emit legendLoaded(outFile);
            StatusMsgHandler::instance()->done();
        }
    }

    reply->deleteLater();
    legendReply_ = nullptr;
}

void MvQWmsUiClient::slotReplyLogo(QNetworkReply* reply)
{
    QNetworkRequest r = reply->request();
    QString mimeType = reply->header(QNetworkRequest::ContentTypeHeader).toString();

    GuiLog().task() << "Load logo";

    if (reply->error() != QNetworkReply::NoError) {
        GuiLog().error() << reply->errorString().toStdString();
        emit logoLoaded(QString());
        StatusMsgHandler::instance()->failed();
    }
    else {
        QString outFile = logoFile_;
        QByteArray ba = reply->readAll();

        if (mimeType.isEmpty())
            mimeType = getMimeType(ba);

        // qDebug() << "mime" << mimeType << getRequestedMimeType(url.toString());

        // If exception is returned or mime type is
        // different to what was requested
        if (mimeType.contains("application/vnd.ogc.se_xml") ||
            mimeType.contains("text/xml"))
        //    || mimeType != getRequestedMimeType(url.toString()))
        {
            QString eMsg;
            readException(ba, eMsg);

            GuiLog().error() << eMsg.toStdString();
            emit logoLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else if (mimeType.contains("text/plain")) {
            QString eMsg(ba);

            GuiLog().error() << eMsg.toStdString();
            emit logoLoaded(QString());
            StatusMsgHandler::instance()->failed();
        }
        else {
            // qDebug() << outFile;

            QFile out(outFile);
            out.open(QIODevice::WriteOnly);
            out.write(ba);
            out.close();

            QImage img(outFile);
            if (img.width() > 100) {
                img.scaledToWidth(100, Qt::SmoothTransformation).save(outFile, "png");
            }

            emit logoLoaded(outFile);
            StatusMsgHandler::instance()->done();
        }
    }

    reply->deleteLater();
    logoReply_ = nullptr;
}


bool MvQWmsUiClient::previewDownloadActive()
{
    return previewReply_ && previewReply_->isRunning();
}

bool MvQWmsUiClient::legendDownloadActive()
{
    return legendReply_ && legendReply_->isRunning();
}

void MvQWmsUiClient::slotAbortDownloadProcess()
{
    if (getCapReply_ && getCapReply_->isRunning()) {
        GuiLog().task() << "Load GetCapabalitites";
        GuiLog().error() << "Interrupted";
        StatusMsgHandler::instance()->failed();

        getCapReply_->deleteLater();
        getCapReply_ = nullptr;
        emit getCapabilityLoaded();
    }
    else if (previewReply_ && previewReply_->isRunning()) {
        GuiLog().task() << "Load preview";
        GuiLog().error() << "Interrupted";
        StatusMsgHandler::instance()->failed();

        previewReply_->deleteLater();
        previewReply_ = nullptr;
        clearPreview();
        emit previewLoaded(previewFile_);
    }
    else if (legendReply_ && legendReply_->isRunning()) {
        GuiLog().task() << "Load legend";
        GuiLog().error() << "Interrupted";
        StatusMsgHandler::instance()->failed();

        legendReply_->deleteLater();
        legendReply_ = nullptr;
        clearLegend();
        emit legendLoaded(legendFile_);
    }
    else if (logoReply_ && logoReply_->isRunning()) {
        GuiLog().task() << "Load logo";
        GuiLog().error() << "Interrupted";
        StatusMsgHandler::instance()->failed();

        logoReply_->deleteLater();
        logoReply_ = nullptr;
        clearLogo();
        emit logoLoaded(logoFile_);
    }
}

QString MvQWmsUiClient::readVersion()
{
    QString result;

    //    QByteArray ba = capability_.toUtf8();
    //    QBuffer buffer(&ba);  // This is a QIODevice.
    //    buffer.open(QIODevice::ReadOnly);

    //    defaultNsDeclaration_ = "declare default element namespace \"http://www.opengis.net/wms\";";
    defaultNsDeclaration_ = "http://www.opengis.net/wms";
    //    auto doc = QDomDocument("getcap");
    //    doc.setContent(ba);
    QDomElement docElem = dom_.documentElement();
    //    qDebug() << "root" << docElem.text() << docElem.tagName();
    if (docElem.tagName() == "WMS_Capabilities") {
        auto attr = docElem.attributes();
        result = attr.namedItem("version").toAttr().value();
        qDebug() << "VERSION" << result;
    }
    else {
        // auto lst = docElem.elementsByTagNameNS(defaultNsDeclaration_, "WMS_Capabilities");
        auto lst = docElem.elementsByTagName("WMS_Capabilities");
        qDebug() << "readVersion";
        if (lst.count() > 0) {
            auto node = lst.item(0);
            auto attr = node.attributes();
            result = attr.namedItem("version").toAttr().value();
            qDebug() << "VERSION NODE:" << node.nodeName();
        }
    }
#if OLD_XML
    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//WMS_Capabilities/data(@version)");
    query.evaluateTo(&result);

    if (result.simplified().isEmpty()) {
        query.setQuery("doc($myDoc)//WMT_MS_Capabilities/data(@version)");
        query.evaluateTo(&result);
        defaultNsDeclaration_.clear();
    }
    buffer.close();
#endif
    // Remove linebreak!!

    return result.simplified();
}

void MvQWmsUiClient::readService()
{
    serviceMeta_.clear();

    QDomElement docElem = dom_.documentElement();
    QDomNode n = docElem.firstChild();
    while (!n.isNull()) {
        if (n.nodeName() == "Service") {
            //              qDebug() << "SERVICE found";
            QStringList tags;
            tags << "Title"
                 << "Abstract"
                 << "Name"
                 << "ContactPerson"
                 << "ContactOrganization"
                 << "ContactPosition"
                 << "AddressType"
                 << "Address"
                 << "City"
                 << "StateOrProvince"
                 << "PostCode"
                 << "Country"
                 << "ContactVoiceTelephone"
                 << "ContactFacsimileTelephone"
                 << "ContactElectronicMailAddress";

            auto elem = n.toElement();
            if (!elem.isNull()) {
                //                qDebug() << "SERVICE not null";
                for (auto name : tags) {
                    //                    qDebug() << "tag" << name;
                    auto lst = elem.elementsByTagName(name);
                    //                    qDebug() << " count" << lst.count();
                    if (lst.count() > 0 && lst.item(0).isElement()) {
                        //                        qDebug() << lst.item(0).isElement() << lst.item(0).isText() << " val:" << lst.item(0).toElement().text();
                        serviceMeta_[name] = lst.item(0).toElement().text().simplified();
                    }
                }
            }
            break;
        }
        n = n.nextSibling();
    }

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    // query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Service/data(Title)");
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Service");
    query.evaluateTo(&result);

    // serviceTitle_=result.simplified();

    QXmlItem item(result.next());
    while (!item.isNull()) {
        if (item.isNode()) {
            query.setFocus(item);

            QString str;

            query.setQuery(defaultNsDeclaration_ + "./data(Title)");
            query.evaluateTo(&str);
            serviceMeta_["Title"] = str.simplified();

            query.setQuery(defaultNsDeclaration_ + "./data(Abstract)");
            query.evaluateTo(&str);
            serviceMeta_["Abstract"] = str.simplified();

            QStringList tags;
            tags << "ContactPerson"
                 << "ContactOrganization"
                 << "ContactPosition"
                 << "AddressType"
                 << "Address"
                 << "City"
                 << "StateOrProvince"
                 << "PostCode"
                 << "Country"
                 << "ContactVoiceTelephone"
                 << "ContactFacsimileTelephone"
                 << "ContactElectronicMailAddress";

            foreach (QString t, tags) {
                str.clear();
                QString q = defaultNsDeclaration_ + ".//data(" + t + ")";
                query.setQuery(q);
                query.evaluateTo(&str);
                serviceMeta_[t] = str.simplified();
            }

            break;
        }
        item = result.next();
    }

    buffer.close();
#endif
    // Remove linebreak!!
}


void MvQWmsUiClient::readFormat()
{
    QStringList formats;
    QString path;
    if (repliedVersion_ > "1.0") {
        path = "Capability/Request/GetMap";
    }
    else {
        path = "Capability/Request/Map";
    }

    auto elem = DomFinder::findElement(dom_.documentElement(), path);
    if (elem.isElement() && !elem.isNull()) {
        auto lst = elem.elementsByTagName("Format");
        for (int i = 0; i < lst.count(); i++) {
            if (lst.item(i).isElement()) {
                formats << lst.item(i).toElement().text().simplified();
            }
        }
    }
    formats.sort();
    format_->setValues(formats);
    format_->setDisplayValues(formats);

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);

    if (repliedVersion_ > "1.0") {
        query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Capability/Request/GetMap/data(Format)");
    }
    else {
        query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Capability/Request/Map/data(Format)");
    }

    query.evaluateTo(&result);

    QStringList lst;
    query.getAtomicValues(result, lst);
    lst.sort();
    format_->setValues(lst);
    format_->setDisplayValues(lst);

    buffer.close();
#endif
}

void MvQWmsUiClient::readLayer()
{
    // Dom
    QDomDocument doc("mydocument");

    if (!doc.setContent(capability_, true)) {
        return;
    }

    QDomElement docElem = doc.documentElement();
    readLayer(docElem, layer_->root());
}

void MvQWmsUiClient::readLayer(QDomElement& docElem, MvQOgcNode* parent)
{
    QDomNode dNode = docElem.firstChild();
    while (!dNode.isNull()) {
        QDomElement dElem = dNode.toElement();
        if (!dElem.isNull()) {
            if (dElem.tagName() == "Layer") {
                auto* layer = new MvQOgcNode;
                parent->addChild(layer);

                QDomNode title = dNode.namedItem("Title");
                if (!title.isNull()) {
                    // qDebug() << title.toElement().text().simplified();
                    layer->setDisplayValue(title.toElement().text().simplified());
                }

                QDomNode name = dNode.namedItem("Name");
                if (!name.isNull()) {
                    layer->setValue(name.toElement().text().simplified());
                }

                QDomNode desc = dNode.namedItem("Abstract");
                if (!desc.isNull()) {
                    layer->setDescription(desc.toElement().text().simplified());
                }
                else if (layer->parent()) {
                    layer->setDescription(layer->parent()->description());
                }

                QDomNamedNodeMap att = dNode.attributes();
                for (int i = 0; i < att.size(); i++) {
                    QDomAttr da = att.item(i).toAttr();
                    if (!da.isNull()) {
                        layer->addAttribute(da.name(), da.value());
                    }
                }

                readLayer(dElem, layer);
            }
            else {
                readLayer(dElem, parent);
            }
        }

        dNode = dNode.nextSibling();
    }
}

void MvQWmsUiClient::readLayerProperties()
{
    if (layer_->currentValue().isEmpty())
        return;

    // find layer root node
    auto elem = DomFinder::findElement(dom_.documentElement(), "Capability/Layer");
    if (!elem.isNull()) {
        // find layer with given name
        auto layerElem = DomFinder::findElementByCondition(elem, "Layer", "Name", layer_->currentValue());
        if (!layerElem.isNull()) {
            // get ancestors in reverse order
            QList<QDomElement> layers;
            while (layerElem.isElement() && layerElem.toElement().tagName() == "Layer") {
                layers.prepend(layerElem);
                layerElem = layerElem.parentNode().toElement();
            }

            readStyle(layers);
            readBoundingBox(layers);  // bounding box should be called before crs!!!!!
            readCrs(layers);
            readGeographicBoundingBox(layers);
            readDimension(layers);
            readAttribution(layers);
        }
    }
}


void MvQWmsUiClient::readStyle(const QList<QDomElement>& layers)
{
    QStringList nameLst, titleLst, abstractLst, legendLst;
    for (auto layer : layers) {
        // collect styles values along the ancestors down from the root layer node
        auto styleElems = DomFinder::findDirectChildren(layer, "Style");
        for (int i = 0; i < styleElems.count(); i++) {
            nameLst << styleElems[i].namedItem("Name").toElement().text().simplified();
            titleLst << styleElems[i].namedItem("Title").toElement().text().simplified();
            abstractLst << styleElems[i].namedItem("Abstract").toElement().text().simplified();
            auto legend = DomFinder::findElement(styleElems[i], "LegendURL/OnlineResource");
            QString legendStr;
            if (legend.isElement() && !legend.isNull()) {
                legendStr = legend.attribute("xlink:href");
            }
            legendLst << legendStr.simplified();
        }
    }

    style_->setValues(nameLst);
    style_->setDisplayValues(titleLst);
    style_->setDescriptions(abstractLst);
    style_->setLegends(legendLst);

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/Style");
    query.evaluateTo(&result);

    QXmlItem item(result.next());
    QStringList nameLst, titleLst, abstractLst, legendLst;
    while (!item.isNull()) {
        if (item.isNode()) {
            QString name, title, abstract, legend;

            // QXmlQuery query;
            query.setFocus(item);

            // Name
            query.setQuery(defaultNsDeclaration_ + "./data(Name)");
            query.evaluateTo(&name);
            nameLst << name.simplified();

            // Title
            query.setQuery(defaultNsDeclaration_ + "./data(Title)");
            query.evaluateTo(&title);
            titleLst << title.simplified();

            // Abstract
            query.setQuery(defaultNsDeclaration_ + "./data(Abstract)");
            query.evaluateTo(&abstract);
            abstractLst << abstract.simplified();

            // Legend url
            query.setQuery(defaultNsDeclaration_ + xlinkNsDeclaration_ + "string(./LegendURL/OnlineResource/@xlink:href)");
            query.evaluateTo(&legend);
            legendLst << legend.simplified();

            style_->setValues(nameLst);
            style_->setDisplayValues(titleLst);
            style_->setDescriptions(abstractLst);
            style_->setLegends(legendLst);
        }
        item = result.next();
    }

    buffer.close();
#endif
}

void MvQWmsUiClient::readCrs(const QList<QDomElement>& layers)
{
    QStringList lst;
    QString crsName = "CRS";
    if (crsRequestName() == "SRS") {
        crsName = "SRS";
    }

    for (auto layer : layers) {
        // collect crs values along the ancestors up to the root layer node
        auto crsElems = DomFinder::findDirectChildren(layer, crsName);
        for (int i = 0; i < crsElems.count(); i++) {
            lst << crsElems[i].text().simplified();
        }
    }

    if (repliedVersion_ <= "1.1.0") {
        QStringList lstOri = lst;
        lst.clear();
        foreach (QString s, lstOri) {
            lst << s.split(" ");
        }
    }
    else if (repliedVersion_ >= "1.3.0") {
        QStringList lstOri = lst;
        lst.clear();
        foreach (QString s, lstOri) {
            if (s == "EPSG:4326" || s == "CRS:84") {
                lst << s;
            }
            else {
                foreach (MvQOgcBoundingBox* bb, bbox_) {
                    if (s == bb->crs()) {
                        lst << s;
                        break;
                    }
                }
            }
        }
    }

    lst.removeDuplicates();
    lst.sort();
    crs_->setValues(lst);
    crs_->setRequestName(crsRequestName());
    crs_->setDisplayName(crsDisplayName());

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));
    // query.bindVariable("myCrsName", QVariant(crsRequestName()));
    if (crsRequestName() == "SRS") {
        query.setQuery("doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/data(SRS)");
    }
    else {
        query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/data(CRS)");
    }

    query.evaluateTo(&result);

    QStringList lst;
    query.getAtomicValues(result, lst);

    if (repliedVersion_ <= "1.1.0") {
        QStringList lstOri = lst;
        lst.clear();
        foreach (QString s, lstOri) {
            lst << s.split(" ");
        }
    }

    if (repliedVersion_ >= "1.3.0") {
        QStringList lstOri = lst;
        lst.clear();
        foreach (QString s, lstOri) {
            if (s == "EPSG:4326" || s == "CRS:84") {
                lst << s;
            }
            else {
                foreach (MvQOgcBoundingBox* bb, bbox_) {
                    if (s == bb->crs()) {
                        lst << s;
                        break;
                    }
                }
            }
        }
    }

    lst.removeDuplicates();
    lst.sort();

    crs_->setValues(lst);
    crs_->setRequestName(crsRequestName());
    crs_->setDisplayName(crsDisplayName());

    buffer.close();
#endif
}

void MvQWmsUiClient::readBoundingBox(const QList<QDomElement>& layers)
{
    for (auto layer : layers) {
        // collect bbox values along the ancestors down from the root layer node
        auto bbElems = DomFinder::findDirectChildren(layer, "BoundingBox");
        for (int i = 0; i < bbElems.count(); i++) {
            QMap<QString, QString> att;
            DomFinder::attributes(bbElems[i], att);
            auto* bb = new MvQOgcBoundingBox(att);
            // Remove all previously defined (in the ancestors) bboxes
            foreach (MvQOgcBoundingBox* bitem, bbox_) {
                if (bitem->crs() == bb->crs()) {
                    bbox_.removeOne(bitem);
                    delete bitem;
                    break;
                }
            }
            bbox_.push_back(bb);
        }
    }

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/BoundingBox");
    query.evaluateTo(&result);

    QXmlItem item(result.next());
    while (!item.isNull()) {
        if (item.isNode()) {
            QMap<QString, QString> att;
            query.getAttributes(item, att);

            MvQOgcBoundingBox* bb = new MvQOgcBoundingBox(att);

            // Remove all previously defined (in the ancestors) bboxes
            foreach (MvQOgcBoundingBox* bitem, bbox_) {
                if (bitem->crs() == bb->crs()) {
                    bbox_.removeOne(bitem);
                    delete bitem;
                    break;
                }
            }

            bbox_.push_back(bb);

            // qDebug() << "bb" <<  bb->crs() << bb->minX() << bb->maxX();
        }
        item = result.next();
    }

    buffer.close();
#endif
}


void MvQWmsUiClient::readGeographicBoundingBox(const QList<QDomElement>& layers)
{
    // get first bounding box from ancestors
    for (auto layer : layers) {
        if (repliedVersion_ >= "1.3.0") {
            auto bb = layer.namedItem("EX_GeographicBoundingBox").toElement();
            if (!bb.isNull()) {
                QStringList names;
                names << "westBoundLongitude"
                      << "eastBoundLongitude"
                      << "southBoundLatitude"
                      << "northBoundLatitude";
                QMap<QString, QString> att;
                for (auto name : names) {
                    att[name] = bb.namedItem(name).toElement().text().simplified();
                }
                if (geographicBbox_) {
                    delete geographicBbox_;
                }
                geographicBbox_ = new MvQOgcGeoBoundingBox(att);  // new MvQXmlElementNode(query,item);
                return;
            }
        }
        else {
            auto bb = layer.namedItem("LatLonBoundingBox").toElement();
            if (!bb.isNull()) {
                QMap<QString, QString> att;
                DomFinder::attributes(bb, att);
                if (geographicBbox_) {
                    delete geographicBbox_;
                }
                geographicBbox_ = new MvQOgcGeoBoundingBox(att);  // new MvQXmlElementNode(query,item);
                return;
            }
        }
    }

#if OLD_XML
    QStringList str;
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));

    if (repliedVersion_ >= "1.3.0") {
        query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/EX_GeographicBoundingBox");
        query.evaluateTo(&result);

        QMap<QString, QString> att;

        QXmlItem item(result.next());
        while (!item.isNull()) {
            if (item.isNode()) {
                QString val;

                // QXmlQuery query;
                query.setFocus(item);

                query.setQuery(defaultNsDeclaration_ + "./data(westBoundLongitude)");
                query.evaluateTo(&val);
                att["westBoundLongitude"] = val.simplified();

                query.setQuery(defaultNsDeclaration_ + "./data(eastBoundLongitude)");
                query.evaluateTo(&val);
                att["eastBoundLongitude"] = val.simplified();

                query.setQuery(defaultNsDeclaration_ + "./data(southBoundLatitude)");
                query.evaluateTo(&val);
                att["southBoundLatitude"] = val.simplified();

                query.setQuery(defaultNsDeclaration_ + "./data(northBoundLatitude)");
                query.evaluateTo(&val);
                att["northBoundLatitude"] = val.simplified();

                if (geographicBbox_) {
                    delete geographicBbox_;
                }
                geographicBbox_ = new MvQOgcGeoBoundingBox(att);  // new MvQXmlElementNode(query,item);
                break;
            }

            item = result.next();
        }
    }
    else {
        query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/LatLonBoundingBox");

        query.evaluateTo(&result);

        QXmlItem item(result.next());
        while (!item.isNull()) {
            if (item.isNode()) {
                QMap<QString, QString> att;
                query.getAttributes(item, att);
                if (geographicBbox_) {
                    delete geographicBbox_;
                }
                geographicBbox_ = new MvQOgcGeoBoundingBox(att);  // new MvQXmlElementNode(query,item);
                break;
            }
            item = result.next();
        }
    }
#endif
}

void MvQWmsUiClient::readDimension(const QList<QDomElement>& layers)
{
    for (auto layer : layers) {
        // collect bbox values along the ancestors down from the root layer node
        auto dimElems = DomFinder::findDirectChildren(layer, "Dimension");
        for (int i = 0; i < dimElems.count(); i++) {
            QMap<QString, QString> att;
            DomFinder::attributes(dimElems[i], att);
            auto* dim = new MvQOgcDimension(att, dimElems[i].text());
            for (int i = 0; i < dimension_.count(); i++) {
                if (dim->name().compare(dimension_[i]->name(), Qt::CaseInsensitive) == 0) {
                    MvQOgcDimension* item = dimension_[i];
                    dimension_.removeAt(i);
                    delete item;
                    break;
                }
            }

            if (dim->name().compare("time", Qt::CaseInsensitive) == 0) {
                dim->setRequestName("TIME");
                dim->setDisplayName("Time:");
            }
            else if (dim->name().compare("elevation", Qt::CaseInsensitive) == 0) {
                dim->setRequestName("ELEVATION");
                dim->setDisplayName("Elevation:");
            }
            else {
                QString dname = dim->name();
                dname = dname.toUpper();
                dname = "DIM_" + dname;
                dim->setRequestName(dname);
                dname += " :";
                dim->setDisplayName(dname);
            }

            dimension_.push_back(dim);
        }
    }

    if (repliedVersion_ < "1.3.0") {
        for (auto layer : layers) {
            // collect bbox values along the ancestors down from the root layer node
            auto dimElems = DomFinder::findDirectChildren(layer, "Extent");
            for (int i = 0; i < dimElems.count(); i++) {
                QMap<QString, QString> att;
                DomFinder::attributes(dimElems[i], att);
                QString value = dimElems[i].text();
                foreach (MvQOgcDimension* dim, dimension_) {
                    if (dim->name() == att["name"]) {
                        dim->setAttributes(att);
                        dim->setValues(value);
                        break;
                    }
                }
            }
        }
    }

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/Dimension");
    query.evaluateTo(&result);

    QXmlItem item(result.next());
    while (!item.isNull()) {
        if (item.isNode()) {
            QMap<QString, QString> att;
            QString value;

            query.getAttributes(item, att);
            query.getAtomicValue(item, value);

            MvQOgcDimension* dim = new MvQOgcDimension(att, value);

            for (int i = 0; i < dimension_.count(); i++) {
                if (dim->name().compare(dimension_[i]->name(), Qt::CaseInsensitive) == 0) {
                    MvQOgcDimension* item = dimension_[i];
                    dimension_.removeAt(i);
                    delete item;
                    break;
                }
            }

            if (dim->name().compare("time", Qt::CaseInsensitive) == 0) {
                dim->setRequestName("TIME");
                dim->setDisplayName("Time:");
            }
            else if (dim->name().compare("elevation", Qt::CaseInsensitive) == 0) {
                dim->setRequestName("ELEVATION");
                dim->setDisplayName("Elevation:");
            }
            else {
                QString dname = dim->name();
                dname = dname.toUpper();
                dname = "DIM_" + dname;
                dim->setRequestName(dname);
                dname += " :";
                dim->setDisplayName(dname);
            }

            dimension_.push_back(dim);
        }
        item = result.next();
    }

    if (repliedVersion_ >= "1.3.0")
        return;

    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/Extent");
    query.evaluateTo(&result);

    item = result.next();
    while (!item.isNull()) {
        if (item.isNode()) {
            QMap<QString, QString> att;
            QString value;

            query.getAttributes(item, att);
            query.getAtomicValue(item, value);

            // MvQXmlElementNode *elem=new MvQXmlElementNode(query,item);
            foreach (MvQOgcDimension* dim, dimension_) {
                if (dim->name() == att["name"]) {
                    dim->setAttributes(att);
                    dim->setValues(value);
                    break;
                }
            }
        }
        item = result.next();
    }
#endif
}

void MvQWmsUiClient::readAttribution(const QList<QDomElement>& layers)
{
    MvQOgcNode* layerNode = layer_->currentNode();
    if (!layerNode)
        return;

    for (auto layer : layers) {
        // collect attribution values along the ancestors down from the root layer node
        auto aElems = DomFinder::findDirectChildren(layer, "Attribution");
        for (int i = 0; i < aElems.count(); i++) {
            auto logoElem = DomFinder::findElement(aElems[i], "LogoURL/OnlineResource");
            if (!logoElem.isNull()) {
                layerNode->setLogo(logoElem.attribute("xlink:href").simplified());
            }
        }
    }

#if OLD_XML
    QXmlResultItems result;

    QByteArray ba = capability_.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    MvQXmlQuery query;
    query.bindVariable("myDoc", &buffer);
    query.bindVariable("myLayer", QVariant(layer_->currentValue()));
    query.setQuery(defaultNsDeclaration_ + "doc($myDoc)//Layer[normalize-space(Name) = $myLayer]/ancestor-or-self::*/Attribution");
    query.evaluateTo(&result);

    QXmlItem item(result.next());
    while (!item.isNull()) {
        if (item.isNode()) {
            QString logo;

            // QXmlQuery query;
            query.setFocus(item);

            // Logo url
            query.setQuery(defaultNsDeclaration_ + xlinkNsDeclaration_ + "string(./LogoURL/OnlineResource/@xlink:href)");
            query.evaluateTo(&logo);
            layerNode->setLogo(logo.simplified());
        }
        item = result.next();
    }
    buffer.close();
#endif
}

void MvQWmsUiClient::readException(QByteArray ba, QString& msg)
{
    // if(xmlMsg.isEmpty())
    //	return;

    QDomDocument doc;
    doc.setContent(ba);

    QString ns;
    if (repliedVersion_ >= "1.3.0")
        ns = "declare default element namespace \"http://www.opengis.net/ogc\";";

    auto elem = DomFinder::findElement(doc.documentElement(), "ServiceExceptionReport");
    if (!elem.isNull()) {
        auto items = elem.elementsByTagName("ServiceException");
        for (int i = 0; i < items.count(); i++) {
            auto exElem = items.item(i).toElement();
            if (!exElem.isNull()) {
                msg += "Exception: ";
                QMap<QString, QString> att;
                QString value = exElem.text();
                DomFinder::attributes(exElem, att);
                msg += att["code"].simplified() + "\n";

                if (!value.isEmpty()) {
                    msg += value;
                    msg += '\n';
                }
            }
        }
    }


#if OLD_XML
    QXmlResultItems result;

    // QByteArray ba=xmlMsg.toUtf8();
    QBuffer buffer(&ba);  // This is a QIODevice.
    buffer.open(QIODevice::ReadOnly);

    QString ns;
    if (repliedVersion_ >= "1.3.0")
        ns = "declare default element namespace \"http://www.opengis.net/ogc\";";

    MvQXmlQuery query;
    query.setMessageHandler(queryMsgHandler_);
    query.bindVariable("myDoc", &buffer);
    query.setQuery(ns + "doc($myDoc)//ServiceExceptionReport/ServiceException");
    query.evaluateTo(&result);

    QXmlItem item(result.next());
    if (item.isNull()) {
        query.setQuery("doc($myDoc)//ServiceExceptionReport/ServiceException");
        query.evaluateTo(&result);
        item = result.next();
    }

    while (!item.isNull()) {
        if (item.isNode()) {
            msg += "Exception: ";

            QMap<QString, QString> att;
            QString value;

            query.getAttributes(item, att);
            query.getAtomicValue(item, value);
            msg += att["code"].simplified() + "\n";

            if (!value.isEmpty()) {
                msg += value;
                msg += '\n';
            }
        }
        item = result.next();
    }

    // If XML parsing failed
#if 0
    if (queryMsgHandler_->fatalFound()) {
        msg += "Exception: parsing of exception document failed!!\n";
        foreach (QString s, queryMsgHandler_->fatalMessages()) {
            msg += s + '\n';
        }
    }

    queryMsgHandler_->clear();
#endif
    buffer.close();
#endif
}

void MvQWmsUiClient::buildGetMapRequest()
{
    if (layer_->currentValue().isEmpty())
        return;

    getMapRequest_->clear();

    QString str = url_;
    str += "?SERVICE=WMS";

    // Version
    /*if(version_ != "Default")
    {
        str+="&VERSION=";
        str+=version_;
    }*/

    str += "&VERSION=";
    str += repliedVersion_;

    // Request
    str += "&REQUEST=GetMap";

    // Layers
    str += "&" + layer_->requestName() + "=" + layer_->currentValue();

    // Style
    str += "&" + style_->requestName() + "=" + style_->currentValue();

    // srs or crs
    str += "&" + crs_->requestName() + "=" + crs_->currentValue();

    // bbox
    str += "&BBOX=" +
           requestBbox_->minX() + "," +
           requestBbox_->minY() + "," +
           requestBbox_->maxX() + "," +
           requestBbox_->maxY();

    // width
    str += "&WIDTH=" + QString::number(width_->value());
    str += "&HEIGHT=" + QString::number(height_->value());

    // Format
    str += "&" + format_->requestName() + "=" + format_->currentValue();

    // Transparent
    str += "&" + transparent_->requestName() + "=" + transparent_->currentValue();

    // Dimensions
    foreach (MvQOgcDimension* dim, dimension_) {
        str += "&";
        str += dim->requestName();
        str += "=";
        str += dim->currentValues();
    }

    if (!extraGetMapPar_.isEmpty()) {
        str += "&" + extraGetMapPar_;
    }

    getMapRequest_->setRequest(str, timeDimName());

    currentGetMapRequest_ = getMapRequest_;

    // qDebug() << getMapRequest_->requestFrame();

    // str+="&stream=false";

    // getMapRequest_=str;

    // saveGetMapRequest(currentGetMapRequest_);

    emit getMapRequestChanged(getMapRequest_->request(), getMapRequest_->requestFrame());
}


//===========================================
//
// Get/Set methods
//
//===========================================

// Format
QStringList MvQWmsUiClient::formatDisplayValues() const
{
    return format_->displayValues();
}

QString MvQWmsUiClient::formatCurrentDisplayValue() const
{
    return format_->currentDisplayValue();
}

void MvQWmsUiClient::slotSetFormatFromDpy(QString value)
{
    if (format_->currentDisplayValue() != value) {
        format_->setCurrentDisplayValue(value);
        buildGetMapRequest();
    }
}

// Transparent
QStringList MvQWmsUiClient::transparentDisplayValues() const
{
    return transparent_->displayValues();
}

QString MvQWmsUiClient::transparentCurrentDisplayValue() const
{
    return transparent_->currentDisplayValue();
}

void MvQWmsUiClient::setTransparentCurrentDisplayValue(QString value)
{
    if (transparent_->currentDisplayValue() != value) {
        transparent_->setCurrentDisplayValue(value);
        buildGetMapRequest();
    }
}

// Width
QString MvQWmsUiClient::width() const
{
    return QString::number(width_->value());
}

void MvQWmsUiClient::setWidth(QString value)
{
    if (width_->value() != value.toInt()) {
        width_->setValue(value.toInt());
        buildGetMapRequest();
    }
}

// Height
QString MvQWmsUiClient::height() const
{
    return QString::number(height_->value());
}

void MvQWmsUiClient::setHeight(QString value)
{
    if (height_->value() != value.toInt()) {
        height_->setValue(value.toInt());
        buildGetMapRequest();
    }
}

// Extra par
void MvQWmsUiClient::setExtraGetCapPar(QString value)
{
    if (extraGetCapPar_ != value) {
        extraGetCapPar_ = value;
    }
}

void MvQWmsUiClient::setExtraGetMapPar(QString value)
{
    if (extraGetMapPar_ != value) {
        extraGetMapPar_ = value;
        buildGetMapRequest();
    }
}

// Style
QStringList MvQWmsUiClient::styleDisplayValues() const
{
    return style_->displayValues();
}

QStringList MvQWmsUiClient::styleDescriptions() const
{
    return style_->descriptions();
}

int MvQWmsUiClient::styleCurrentIndex() const
{
    return style_->currentIndex();
}

void MvQWmsUiClient::setStyleCurrentIndex(int index)
{
    if (style_->currentIndex() != index) {
        style_->setCurrentIndex(index);
        buildGetMapRequest();
    }
}


// CRS
QStringList MvQWmsUiClient::crsDisplayValues() const
{
    return crs_->displayValues();
}

QString MvQWmsUiClient::crsCurrentDisplayValue() const
{
    return crs_->currentDisplayValue();
}

void MvQWmsUiClient::setCrsCurrentDisplayValue(QString value)
{
    if (crs_->currentDisplayValue() != value) {
        slotAbortDownloadProcess();
        crs_->setCurrentDisplayValue(value);
        updatRequestBoundingBox();
        buildGetMapRequest();
    }
}

// Layer
int MvQWmsUiClient::layerCurrentIndex()
{
    return 0;
    // return layer_->values().indexOf(layer_->currentValue());
}

QStringList MvQWmsUiClient::layerDisplayValues() const
{
    return {};
    // return layer_->displayValues();
}

QStringList MvQWmsUiClient::layerDescriptions() const
{
    return {};
    // qDebug() << layer_->descriptions();
    // return layer_->descriptions();
}

void MvQWmsUiClient::setLayerFromDpy(MvQOgcNode* node)
{
    slotAbortDownloadProcess();

    clearLayerDependentData();

    layer_->setCurrentNode(node);

    lastGetMapRequest_->setRequest(getMapRequest_->request());

    updateAfterLayerLoaded();
}


// BBOX
void MvQWmsUiClient::setBoundingBoxFromDpy(QString minX, QString maxX, QString minY, QString maxY)
{
    slotAbortDownloadProcess();
    requestBbox_->setArea(minX, maxX, minY, maxY);
    buildGetMapRequest();
}

MvQOgcBoundingBox MvQWmsUiClient::lastBoundingBox() const
{
    QString lastValue = lastGetMapRequest_->item("BBOX");
    QStringList lst = lastValue.split(",");
    MvQOgcBoundingBox bb;
    if (lst.count() == 4) {
        bb.setArea(lst[0], lst[2], lst[1], lst[3]);
    }
    return bb;
}

MvQOgcBoundingBox* MvQWmsUiClient::currentBoundingBox() const
{
    for (int i = bbox_.count() - 1; i >= 0; i--) {
        if (bbox_[i]->crs() == crs_->currentValue()) {
            return bbox_[i];
        }
    }

    return nullptr;
}

// Dimensions
QStringList MvQWmsUiClient::dimensionNameToDpy() const
{
    QStringList lst;
    /*foreach(MvQOgcDimensionList *dim,dimension_)
    {
        lst << dim->displayName();
    }*/

    return lst;
}

QStringList MvQWmsUiClient::dimensionToDpy(QString /*dimDpyName*/) const
{
    /*foreach(MvQOgcDimensionList *dim,dimension_)
    {
        if(dim->displayName() == dimDpyName)
        {
        return dim->displayValues();
        }
    }*/

    return {};
}

void MvQWmsUiClient::setDimensionFromDpy(QString dimDpyName, QString val)
{
    slotAbortDownloadProcess();

    foreach (MvQOgcDimension* dim, dimension_) {
        if (dim->displayName() == dimDpyName) {
            // dim->setCurrentDisplayValue(val);
            dim->setCurrentValues(val);
            buildGetMapRequest();
            return;
        }
    }
}

void MvQWmsUiClient::setDimensionFromDpy(QString /*dimDpyName*/, QStringList /*val*/)
{
    /*foreach(MvQOgcDimensionList *dim,dimension_)
    {
        if(dim->displayName() == dimDpyName)
        {
        //dim->setCurrentDisplayValues(val);
        setCurrentValues(val);
        buildGetMapRequest();
        return;
        }
    }*/
}

QString MvQWmsUiClient::crsQueryName()
{
    if (repliedVersion_ < QString("1.3.0")) {
        return {"SRS"};
    }
    else {
        return {"CRS"};
    }
}

QString MvQWmsUiClient::crsRequestName()
{
    if (repliedVersion_ < QString("1.3.0")) {
        return {"SRS"};
    }
    else {
        return {"CRS"};
    }
}

QString MvQWmsUiClient::crsDisplayName()
{
    if (repliedVersion_ < QString("1.3.0")) {
        return {"SRS:"};
    }
    else {
        return {"CRS:"};
    }
}

QStringList MvQWmsUiClient::timeDimName()
{
    QStringList timeDimName;

    foreach (MvQOgcDimension* dim, dimension_) {
        if (dim->isTime()) {
            timeDimName << dim->requestName();
        }
    }
    return timeDimName;
}

void MvQWmsUiClient::clearPreview()
{
    // if(previewFile_ != emptyFileNameString_)
    //{
    QFile f(previewFile_);

    if (!f.remove()) {
        // qDebug() << "MvQZoomStackLevel::clearPreview() ---> Preview file" << imgFileName_ << "could not be deleted!";
    }
    // previewFile_ = emptyFileNameString_;
    // }
}

void MvQWmsUiClient::clearLegend()
{
    // if(previewFile_ != emptyFileNameString_)
    //{
    QFile f(legendFile_);

    if (!f.remove()) {
        // qDebug() << "MvQZoomStackLevel::clearPreview() ---> Preview file" << imgFileName_ << "could not be deleted!";
    }
    // previewFile_ = emptyFileNameString_;
    // }
}

void MvQWmsUiClient::clearLogo()
{
    QFile f(logoFile_);

    if (!f.remove()) {
    }
}


void MvQWmsUiClient::updatRequestBoundingBox()
{
    requestBbox_->clear();
    bool foundBb = false;

    // Check if bbox is available for the current crs.
    // It is mandatory from 1.3.0 !!!
    foreach (MvQOgcBoundingBox* bb, bbox_) {
        if (bb->crs() == crs_->currentValue()) {
            foundBb = true;
            requestBbox_->setArea(bb->minX(), bb->maxX(), bb->minY(), bb->maxY());
            break;
        }
    }

    if (!foundBb && geographicBbox_ != nullptr) {
        // For 1.3.0 epsg:4326 lat is x and lon is y!!!!!
        if (repliedVersion_ >= "1.3.0") {
            if (crs_->currentValue() == "EPSG:4326") {
                requestBbox_->setArea(geographicBbox_->minY(),
                                      geographicBbox_->maxY(),
                                      geographicBbox_->minX(),
                                      geographicBbox_->maxX());

                foundBb = true;
            }
            else if (crs_->currentValue() == "CRS:84") {
                requestBbox_->setArea(geographicBbox_->minX(),
                                      geographicBbox_->maxX(),
                                      geographicBbox_->minY(),
                                      geographicBbox_->maxY());

                foundBb = true;
            }
        }
        else {
            // Else for older versions lat-lon we try to use the geographic bounding box
            requestBbox_->setArea(geographicBbox_->minX(),
                                  geographicBbox_->maxX(),
                                  geographicBbox_->minY(),
                                  geographicBbox_->maxY());

            foundBb = true;
        }
    }

    // For special non-geographical projections where no crs is defined (e.g. cross-section)
    if (!foundBb && crs_->currentValue().isEmpty() && geographicBbox_ != nullptr) {
        requestBbox_->setArea(geographicBbox_->minX(),
                              geographicBbox_->maxX(),
                              geographicBbox_->minY(),
                              geographicBbox_->maxY());
    }
}
