// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2024, Oslandia.
// Copyright (c) 2024-2026, SFCGAL team.
// SPDX-License-Identifier: LGPL-2.0-or-later

#include "SFCGAL/Geometry.h"

#include "SFCGAL/GeometryVisitor.h"
#include "SFCGAL/Point.h"
#include "SFCGAL/detail/GetPointsVisitor.h"
#include "SFCGAL/detail/io/WkbWriter.h"
#include "SFCGAL/detail/io/WktWriter.h"

#include "SFCGAL/algorithm/BoundaryVisitor.h"
#include "SFCGAL/algorithm/centroid.h"
#include "SFCGAL/algorithm/distance.h"
#include "SFCGAL/algorithm/distance3d.h"

#include "SFCGAL/detail/EnvelopeVisitor.h"

#include "SFCGAL/detail/transform/RoundTransform.h"

#include "SFCGAL/Kernel.h"

namespace SFCGAL {

auto
Geometry::asText(const int &numDecimals) const -> std::string
{
  std::ostringstream oss;

  if (numDecimals >= 0) {
    oss << std::fixed;
    oss.precision(numDecimals);
  }

  detail::io::WktWriter writer(oss);
  bool                  exact = false;

  if (numDecimals == -1) {
    exact = true;
  }

  writer.write(*this, exact);
  return oss.str();
}

auto
Geometry::asWkb(boost::endian::order wkbOrder, bool asHex) const -> std::string
{
  std::ostringstream    oss;
  detail::io::WkbWriter writer(oss, asHex);
  writer.write(*this, wkbOrder);
  return oss.str();
}
auto
Geometry::envelope() const -> Envelope
{
  Envelope                box;
  detail::EnvelopeVisitor envelopeVisitor(box);
  accept(envelopeVisitor);
  return box;
}

auto
Geometry::boundary() const -> std::unique_ptr<Geometry>
{
  algorithm::BoundaryVisitor visitor;
  accept(visitor);
  return std::unique_ptr<Geometry>(visitor.releaseBoundary());
}

auto
Geometry::distance(const Geometry &other) const -> double
{
  return algorithm::distance(*this, other);
}

auto
Geometry::distance3D(const Geometry &other) const -> double
{
  return algorithm::distance3D(*this, other);
}

void
Geometry::round(const long &scale)
{
  transform::RoundTransform roundTransform(scale);
  accept(roundTransform);
}

auto
Geometry::numGeometries() const -> size_t
{
  return isEmpty() ? 0 : 1;
}

auto
Geometry::geometryN(size_t const &idx) const -> const Geometry &
{
  BOOST_ASSERT(idx == 0);
  (void)idx;
  return *this;
}

auto
Geometry::geometryN(size_t const &idx) -> Geometry &
{
  BOOST_ASSERT(idx == 0);
  (void)idx;
  return *this;
}

void
Geometry::setGeometryN(const Geometry &geometry, size_t const &idx)
{
  (void)geometry;
  (void)idx;
}

void
Geometry::setGeometryN(Geometry *geometry, size_t const &idx)
{
  (void)geometry;
  (void)idx;
}

void
Geometry::setGeometryN([[maybe_unused]] std::unique_ptr<Geometry> geometry,
                       [[maybe_unused]] size_t const             &idx)
{
}

Geometry::Geometry() = default;

auto
Geometry::hasValidityFlag() const -> bool
{
  return validityFlag_;
}

void
Geometry::forceValidityFlag(bool valid)
{
  validityFlag_ = valid;
}

auto
Geometry::almostEqual(const Geometry &other, const double tolerance,
                      algorithm::EqualityStrictness strictOrder) const -> bool
{
  return algorithm::almostEqual(*this, other, tolerance, strictOrder);
}

auto
Geometry::centroid() const -> Point
{
  std::unique_ptr<Point> out = algorithm::centroid(*this);

  return *out;
}

auto
Geometry::centroid3D() const -> Point
{
  std::unique_ptr<Point> out = algorithm::centroid3D(*this);

  return *out;
}

/// Function used to compare geometries
/// FIXME
/// Since we do not have (yet) a real "equals" operator, we only compare points
/// coordinates
auto
operator==(const Geometry &geomA, const Geometry &geomB) -> bool
{
  return algorithm::almostEqual(
      geomA, geomB, -1.0, algorithm::EqualityStrictness::pointNonOrdered());
}

} // namespace SFCGAL
