/* ============================================================
 *
 * This file is a part of digiKam project
 * https://www.digikam.org
 *
 * Date        : 2023-05-15
 * Description : geolocation engine based on Marble.
 *               (c) 2007-2022 Marble Team
 *               https://invent.kde.org/education/marble/-/raw/master/data/credits_authors.html
 *
 * SPDX-FileCopyrightText: 2023-2024 by Gilles Caulier <caulier dot gilles at gmail dot com>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 * ============================================================ */

#include "GeoDataRegion.h"
#include "GeoDataRegion_p.h"

// C++ includes

#include <algorithm>

// Local includes

#include "GeoDataFeature.h"
#include "GeoDataPlacemark.h"
#include "GeoDataGeometry.h"
#include "GeoDataTypes.h"

namespace Marble
{
GeoDataRegion::GeoDataRegion()
    : GeoDataObject(),
      d(new GeoDataRegionPrivate)
{
}

GeoDataRegion::GeoDataRegion(const GeoDataRegion& other)
    : GeoDataObject(other),
      d(new GeoDataRegionPrivate(*other.d))
{
}

GeoDataRegion::GeoDataRegion(GeoDataFeature* feature)
    : GeoDataObject(),
      d(new GeoDataRegionPrivate(feature))
{
}


GeoDataRegion::~GeoDataRegion()
{
    delete d;
}


const char* GeoDataRegion::nodeType() const
{
    return GeoDataTypes::GeoDataRegionType;
}

bool GeoDataRegion::operator==(const GeoDataRegion& other) const
{
    return equals(other)
           && this->latLonAltBox() == other.latLonAltBox()
           && this->lod() == other.lod();
}

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

const GeoDataLatLonAltBox& GeoDataRegion::latLonAltBox() const
{
    // FIXME: This isn't exactly what a 'const' function should do, is it?

    // If the latLonAltBox hasn't been set try to determine it automatically
    if (!d->m_latLonAltBox)
    {
        // If there is a parent try to
        if (d->m_parent)
        {

            if (const GeoDataPlacemark* placemark = geodata_cast<GeoDataPlacemark>(d->m_parent))
            {
                const GeoDataGeometry* geometry = placemark->geometry();

                if (geometry)
                {
                    d->m_latLonAltBox = new GeoDataLatLonAltBox(placemark->geometry()->latLonAltBox());
                }

                else
                {
                    d->m_latLonAltBox = new GeoDataLatLonAltBox();
                }
            }

            else
            {
                // If the parent is not a placemark then create a default LatLonAltBox
                // FIXME: reference a shared object instead
                d->m_latLonAltBox = new GeoDataLatLonAltBox();
            }
        }

        else
        {
            // If there is no parent then create a default LatLonAltBox
            // FIXME: reference a shared object instead
            d->m_latLonAltBox = new GeoDataLatLonAltBox();
        }
    }

    return *(d->m_latLonAltBox);
}


void GeoDataRegion::setLatLonAltBox(const GeoDataLatLonAltBox& latLonAltBox)
{
    delete d->m_latLonAltBox;
    d->m_latLonAltBox = new GeoDataLatLonAltBox(latLonAltBox);
}


GeoDataLod& GeoDataRegion::lod() const
{
    // If the lod hasn't been set then return a shared one
    if (!d->m_lod)
    {
        // FIXME: reference a shared object instead
        d->m_lod = new GeoDataLod();
    }

    return *(d->m_lod);
}


void GeoDataRegion::setLod(const GeoDataLod& lod)
{
    delete d->m_lod;
    d->m_lod = new GeoDataLod(lod);
}


void GeoDataRegion::pack(QDataStream& stream) const
{
    GeoDataObject::pack(stream);

    d->m_lod->pack(stream);
    d->m_latLonAltBox->pack(stream);
}


void GeoDataRegion::unpack(QDataStream& stream)
{
    GeoDataObject::unpack(stream);

    d->m_lod->unpack(stream);
    d->m_latLonAltBox->unpack(stream);
}

GeoDataRegion& GeoDataRegion::operator=(const GeoDataRegion& other)
{
    // Self assignment
    if (this == &other)
    {
        return *this;
    }

    GeoDataRegion temp(other);
    swap(temp);
    return *this;
}

void GeoDataRegion::swap(GeoDataRegion& other)
{
    std::swap(d, other.d);
}

} // namespace Marble
