// Copyright (c) 1997
// Utrecht University (The Netherlands),
// ETH Zurich (Switzerland),
// INRIA Sophia-Antipolis (France),
// Max-Planck-Institute Saarbruecken (Germany),
// and Tel-Aviv University (Israel).  All rights reserved.
//
// This file is part of CGAL (www.cgal.org);
//
// $URL: https://github.com/CGAL/cgal/blob/v6.2/Stream_support/include/CGAL/IO/PLY/write_ply_points.h $
// $Id: include/CGAL/IO/PLY/write_ply_points.h cac3e9d75e2 $
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
//
// Author(s) : Simon Giraudot

#ifndef CGAL_IO_PLY_WRITE_PLY_POINTS_H
#define CGAL_IO_PLY_WRITE_PLY_POINTS_H

#include <CGAL/IO/helpers.h>
#include <CGAL/IO/PLY.h>

#include <CGAL/property_map.h>
#include <CGAL/assertions.h>
#include <CGAL/Iterator_range.h>

#include <CGAL/Named_function_parameters.h>
#include <CGAL/boost/graph/named_params_helper.h>

#include <boost/version.hpp>

#include <iostream>
#include <fstream>
#include <iterator>
#include <tuple>
#include <type_traits>

namespace CGAL {

namespace IO {

  // documented in ../PLY.h
  template <typename PointRange,
          typename ... PropertyHandler>
  bool write_PLY_with_properties(std::ostream& os, ///< output stream.
                                 const PointRange& points, ///< input point range.
                                 PropertyHandler&& ... properties) ///< parameter pack of property handlers
{
  CGAL_precondition(points.begin() != points.end());

  if(!os)
  {
    std::cerr << "Error: cannot open file" << std::endl;
    return false;
  }

  // Write header
  os << "ply" << std::endl
     << ((get_mode(os) == BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
     << "comment Generated by the CGAL library" << std::endl
     << "element vertex " << points.size() << std::endl;

  internal::output_property_header (os, std::forward<PropertyHandler>(properties)...);

  os << "end_header" << std::endl;

  // Write positions + normals
  for(typename PointRange::const_iterator it = points.begin(); it != points.end(); it++)
    internal::output_properties (os, it, std::forward<PropertyHandler>(properties)...);

  return !os.fail();
}

// documented in ../PLY.h
template <typename PointRange, typename CGAL_NP_TEMPLATE_PARAMETERS_NO_DEFAULT>
bool write_PLY(std::ostream& os,
               const PointRange& points,
               const CGAL_NP_CLASS& np
               , std::enable_if_t<internal::is_Range<PointRange>::value>*
               )
{
  using parameters::choose_parameter;
  using parameters::get_parameter;

  // basic geometric types
  typedef Point_set_processing_3_np_helper<PointRange, CGAL_NP_CLASS> NP_helper;
  typedef typename NP_helper::Const_point_map PointMap;
  typedef typename NP_helper::Normal_map NormalMap;

  const bool has_normals = NP_helper::has_normal_map(points, np);

  PointMap point_map = NP_helper::get_const_point_map(points, np);
  NormalMap normal_map = NP_helper::get_normal_map(points, np);

  if(!os)
  {
    std::cerr << "Error: cannot open file" << std::endl;
    return false;
  }

  set_stream_precision_from_NP(os, np);

  if(has_normals)
    return write_PLY_with_properties(os, points,
                                     make_ply_point_writer(point_map),
                                     make_ply_normal_writer(normal_map));

  return write_PLY_with_properties(os, points, make_ply_point_writer(point_map));
}

// documented in ../PLY.h
template <typename PointRange, typename CGAL_NP_TEMPLATE_PARAMETERS_NO_DEFAULT>
bool write_PLY(const std::string& filename,
               const PointRange& points,
               const CGAL_NP_CLASS& np,
               std::enable_if_t<internal::is_Range<PointRange>::value>*
               )
{
  const bool binary = CGAL::parameters::choose_parameter(CGAL::parameters::get_parameter(np, internal_np::use_binary_mode), true);
  if(binary)
  {
    std::ofstream os(filename, std::ios::binary);
    CGAL::IO::set_mode(os, CGAL::IO::BINARY);
    return write_PLY(os, points, np);
  }
  else
  {
    std::ofstream os(filename);
    CGAL::IO::set_mode(os, CGAL::IO::ASCII);
    return write_PLY(os, points, np);
  }
}

} // namespace IO

} // namespace CGAL

#endif // CGAL_IO_PLY_WRITE_PLY_POINTS_H
