/*
    SPDX-FileCopyrightText: 2007 Murad Tagirov <tmurad@gmail.com>
    SPDX-FileCopyrightText: 2007 Nikolas Zimmermann <zimmermann@kde.org>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "GeoDataDocument.h"
#include "GeoDataDocument_p.h"

#include "GeoDataNetworkLinkControl.h"
#include "GeoDataSchema.h"
#include "GeoDataStyleMap.h"

#include "MarbleDebug.h"

#include <QDataStream>

namespace Marble
{

GeoDataDocument::GeoDataDocument()
    : GeoDataContainer(new GeoDataDocumentPrivate)
{
}

GeoDataDocument::GeoDataDocument(const GeoDataDocument &other)
    : GeoDocument()
    , GeoDataContainer(other, new GeoDataDocumentPrivate(*other.d_func()))
{
}

GeoDataDocument::~GeoDataDocument() = default;

GeoDataDocument &GeoDataDocument::operator=(const GeoDataDocument &other)
{
    if (this != &other) {
        Q_D(GeoDataDocument);
        *d = *other.d_func();
    }

    return *this;
}

bool GeoDataDocument::operator==(const GeoDataDocument &other) const
{
    if (!GeoDataContainer::equals(other)) {
        return false;
    }

    Q_D(const GeoDataDocument);
    const GeoDataDocumentPrivate *const other_d = other.d_func();
    if (!(d->m_styleHash.size() == other_d->m_styleHash.size() && d->m_styleMapHash == other_d->m_styleMapHash && d->m_schemaHash == other_d->m_schemaHash
          && d->m_filename == other_d->m_filename && d->m_baseUri == other_d->m_baseUri && d->m_networkLinkControl == other_d->m_networkLinkControl
          && d->m_property == other_d->m_property && d->m_documentRole == other_d->m_documentRole)) {
        return false;
    }

    auto iter = d->m_styleHash.constBegin();
    auto const end = d->m_styleHash.constEnd();
    for (; iter != end; ++iter) {
        if (!other_d->m_styleHash.contains(iter.key())) {
            return false;
        }

        if (*iter.value() != *other_d->m_styleHash[iter.key()]) {
            return false;
        }
    }

    return true;
}

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

const char *GeoDataDocument::nodeType() const
{
    return GeoDataTypes::GeoDataDocumentType;
}

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

DocumentRole GeoDataDocument::documentRole() const
{
    Q_D(const GeoDataDocument);
    return d->m_documentRole;
}

void GeoDataDocument::setDocumentRole(DocumentRole role)
{
    Q_D(GeoDataDocument);
    d->m_documentRole = role;
}

QString GeoDataDocument::property() const
{
    Q_D(const GeoDataDocument);
    return d->m_property;
}

void GeoDataDocument::setProperty(const QString &property)
{
    Q_D(GeoDataDocument);
    d->m_property = property;
}

QString GeoDataDocument::fileName() const
{
    Q_D(const GeoDataDocument);
    return d->m_filename;
}

void GeoDataDocument::setFileName(const QString &value)
{
    Q_D(GeoDataDocument);
    d->m_filename = value;
}

QString GeoDataDocument::baseUri() const
{
    Q_D(const GeoDataDocument);
    return d->m_baseUri;
}

void GeoDataDocument::setBaseUri(const QString &baseUrl)
{
    Q_D(GeoDataDocument);
    d->m_baseUri = baseUrl;
}

GeoDataNetworkLinkControl GeoDataDocument::networkLinkControl() const
{
    Q_D(const GeoDataDocument);
    return d->m_networkLinkControl;
}

void GeoDataDocument::setNetworkLinkControl(const GeoDataNetworkLinkControl &networkLinkControl)
{
    Q_D(GeoDataDocument);
    d->m_networkLinkControl = networkLinkControl;
}

void GeoDataDocument::addStyle(const GeoDataStyle::Ptr &style)
{
    Q_D(GeoDataDocument);
    d->m_styleHash.insert(style->id(), style);
    d->m_styleHash[style->id()]->setParent(this);
}

void GeoDataDocument::removeStyle(const QString &styleId)
{
    Q_D(GeoDataDocument);
    d->m_styleHash.remove(styleId);
}

GeoDataStyle::Ptr GeoDataDocument::style(const QString &styleId)
{
    /*
     * FIXME: m_styleHash always should contain at least default
     *        GeoDataStyle element
     */
    Q_D(GeoDataDocument);
    return d->m_styleHash[styleId];
}

GeoDataStyle::ConstPtr GeoDataDocument::style(const QString &styleId) const
{
    Q_D(const GeoDataDocument);
    return d->m_styleHash.value(styleId);
}

QList<GeoDataStyle::ConstPtr> GeoDataDocument::styles() const
{
    Q_D(const GeoDataDocument);
    QList<GeoDataStyle::ConstPtr> result;
    for (auto const &style : d->m_styleHash.values()) {
        result << style;
    }

    return result;
}

QList<GeoDataStyle::Ptr> GeoDataDocument::styles()
{
    Q_D(GeoDataDocument);
    return d->m_styleHash.values();
}

void GeoDataDocument::addStyleMap(const GeoDataStyleMap &map)
{
    Q_D(GeoDataDocument);
    d->m_styleMapHash.insert(map.id(), map);
    d->m_styleMapHash[map.id()].setParent(this);
}

void GeoDataDocument::removeStyleMap(const QString &mapId)
{
    Q_D(GeoDataDocument);
    d->m_styleMapHash.remove(mapId);
}

GeoDataStyleMap &GeoDataDocument::styleMap(const QString &styleId)
{
    Q_D(GeoDataDocument);
    return d->m_styleMapHash[styleId];
}

GeoDataStyleMap GeoDataDocument::styleMap(const QString &styleId) const
{
    Q_D(const GeoDataDocument);
    return d->m_styleMapHash.value(styleId);
}

QList<GeoDataStyleMap> GeoDataDocument::styleMaps() const
{
    Q_D(const GeoDataDocument);
    return d->m_styleMapHash.values();
}

void GeoDataDocument::addSchema(const GeoDataSchema &schema)
{
    Q_D(GeoDataDocument);
    d->m_schemaHash.insert(schema.id(), schema);
    d->m_schemaHash[schema.id()].setParent(this);
}

void GeoDataDocument::removeSchema(const QString &schemaId)
{
    Q_D(GeoDataDocument);
    GeoDataSchema schema = d->m_schemaHash.take(schemaId);
    schema.setParent(nullptr);
}

GeoDataSchema GeoDataDocument::schema(const QString &schemaId) const
{
    Q_D(const GeoDataDocument);
    return d->m_schemaHash.value(schemaId);
}

GeoDataSchema &GeoDataDocument::schema(const QString &schemaId)
{
    Q_D(GeoDataDocument);
    return d->m_schemaHash[schemaId];
}

QList<GeoDataSchema> GeoDataDocument::schemas() const
{
    Q_D(const GeoDataDocument);
    return d->m_schemaHash.values();
}

void GeoDataDocument::pack(QDataStream &stream) const
{
    Q_D(const GeoDataDocument);
    GeoDataContainer::pack(stream);

    stream << d->m_styleHash.size();

    for (QMap<QString, GeoDataStyle::Ptr>::const_iterator iterator = d->m_styleHash.constBegin(); iterator != d->m_styleHash.constEnd(); ++iterator) {
        iterator.value()->pack(stream);
    }
}

void GeoDataDocument::unpack(QDataStream &stream)
{
    Q_D(GeoDataDocument);
    GeoDataContainer::unpack(stream);

    int size = 0;

    stream >> size;
    for (int i = 0; i < size; i++) {
        GeoDataStyle::Ptr style(new GeoDataStyle);
        style->unpack(stream);
        d->m_styleHash.insert(style->id(), style);
    }
}

}
