/**********************************************************************
 *
 * GEOS - Geometry Engine Open Source
 * http://geos.osgeo.org
 *
 * Copyright (C) 2011 Sandro Santilli <strk@kbt.io>
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
 *
 * This is free software; you can redistribute and/or modify it under
 * the terms of the GNU Lesser General Public Licence as published
 * by the Free Software Foundation.
 * See the COPYING file for more information.
 *
 **********************************************************************
 *
 * Last port: geom/MultiPolygon.java r320 (JTS-1.12)
 *
 **********************************************************************/

#include <geos/geom/Geometry.h>
#include <geos/geom/LineString.h>
#include <geos/geom/Polygon.h>
#include <geos/geom/MultiPolygon.h>
#include <geos/geom/MultiLineString.h>
#include <geos/geom/MultiSurface.h>
#include <geos/geom/GeometryFactory.h>
#include <geos/geom/Dimension.h>

#include <cassert>
#include <string>
#include <vector>
#include <algorithm>


namespace geos {
namespace geom { // geos::geom

/*protected*/
MultiPolygon::MultiPolygon(std::vector<std::unique_ptr<Polygon>> && newPolys, const GeometryFactory& factory)
      : GeometryCollection(std::move(newPolys), factory)
{}

MultiPolygon::MultiPolygon(std::vector<std::unique_ptr<Geometry>> && newPolys, const GeometryFactory& factory)
        : GeometryCollection(std::move(newPolys), factory)
{}

MultiPolygon::~MultiPolygon() {}

Dimension::DimensionType
MultiPolygon::getDimension() const
{
    return Dimension::A; // area
}

int
MultiPolygon::getBoundaryDimension() const
{
    return 1;
}

std::string
MultiPolygon::getGeometryType() const
{
    return "MultiPolygon";
}

std::unique_ptr<Geometry>
MultiPolygon::getBoundary() const
{
    if(isEmpty()) {
        return std::unique_ptr<Geometry>(getFactory()->createMultiLineString());
    }

    std::vector<std::unique_ptr<Geometry>> allRings;
    for(const auto& pg : geometries) {
        auto g = pg->getBoundary();

        if(g->getNumGeometries() == 1) {
            allRings.push_back(std::move(g));
        } else {
            for(std::size_t i = 0; i < g->getNumGeometries(); ++i) {
                // TODO avoid this clone
                allRings.push_back(g->getGeometryN(i)->clone());
            }
        }
    }

    return getFactory()->createMultiLineString(std::move(allRings));
}

GeometryCollection*
MultiPolygon::getCurvedImpl(const algorithm::LineToCurveParams& params) const {
    std::vector<std::unique_ptr<Geometry>> curvedGeoms(geometries.size());

    bool hasCurves = false;

    for (std::size_t i = 0; i < geometries.size(); i++) {
        curvedGeoms[i] = geometries[i]->getCurved(params);
        if (curvedGeoms[i]->hasCurvedComponents()) {
            hasCurves = true;
        }
    }

    if (hasCurves) {
        return getFactory()->createMultiSurface(std::move(curvedGeoms)).release();
    }

    return getFactory()->createMultiPolygon(std::move(curvedGeoms)).release();
}

GeometryTypeId
MultiPolygon::getGeometryTypeId() const
{
    return GEOS_MULTIPOLYGON;
}

MultiPolygon*
MultiPolygon::reverseImpl() const
{
    if(isEmpty()) {
        return clone().release();
    }

    std::vector<std::unique_ptr<Geometry>> reversed(geometries.size());

    std::transform(geometries.begin(),
                   geometries.end(),
                   reversed.begin(),
    [](const std::unique_ptr<Geometry> & g) {
        return g->reverse();
    });

    return getFactory()->createMultiPolygon(std::move(reversed)).release();
}

const Polygon*
MultiPolygon::getGeometryN(std::size_t i) const
{
    return static_cast<const Polygon*>(geometries[i].get());
}

} // namespace geos::geom
} // namespace geos
