/***************************************************************************
                          qgsvectorfilewriter.h
                          generic vector file writer
                             -------------------
    begin                : Jun 6 2004
    copyright            : (C) 2004 by Tim Sutton
    email                : tim at linfiniti.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#ifndef QGSVECTORFILEWRITER_H
#define QGSVECTORFILEWRITER_H

#include <ogr_api.h>

#include "qgis_core.h"
#include "qgis_sip.h"
#include "qgsabstractdatabaseproviderconnection.h"
#include "qgsfeaturesink.h"
#include "qgsfeedback.h"
#include "qgsfields.h"
#include "qgsgeometryengine.h"
#include "qgsogrutils.h"
#include "qgsrendercontext.h"
#include "qgsrenderer.h"

#include <QString>

using namespace Qt::StringLiterals;

class QgsSymbolLayer;
class QTextCodec;
class QgsFeatureIterator;

/**
 * \ingroup core
 * \brief A convenience class for writing vector layers to disk based formats (e.g. Shapefiles, GeoPackage).
 *
 * There are two possibilities how to use this class:
 *
 * 1. A static call to QgsVectorFileWriter::writeAsVectorFormat(...) which saves the whole vector layer.
 * 2. Create an instance of the class and issue calls to addFeature(...).
 */
class CORE_EXPORT QgsVectorFileWriter : public QgsFeatureSink
{
  public:
    enum OptionType
    {
      Set,
      String,
      Int,
      Hidden
    };

    /**
     * \ingroup core
     * \brief Describes an available option for configuring file writing for a particular output format.
     */
    class Option
    {
      public:
        Option( const QString &docString, QgsVectorFileWriter::OptionType type )
          : docString( docString )
          , type( type )
        {}
        virtual ~Option() = default;

        QString docString;
        QgsVectorFileWriter::OptionType type;
    };

    /**
     * \ingroup core
     * \brief An available option for configuring file writing for a particular output format, presenting a choice of preset values.
     */
    class SetOption : public QgsVectorFileWriter::Option
    {
      public:
        SetOption( const QString &docString, const QStringList &values, const QString &defaultValue, bool allowNone = false )
          : Option( docString, Set )
          , values( values.begin(), values.end() )
          , defaultValue( defaultValue )
          , allowNone( allowNone )
        {}

        QSet<QString> values;
        QString defaultValue;
        bool allowNone;
    };

    /**
     * \ingroup core
     * \brief An available option for configuring file writing for a particular output format, presenting a freeform string option.
     */
    class StringOption : public QgsVectorFileWriter::Option
    {
      public:
        StringOption( const QString &docString, const QString &defaultValue = QString() )
          : Option( docString, String )
          , defaultValue( defaultValue )
        {}

        QString defaultValue;
    };

    /**
     * \ingroup core
     * \brief An available option for configuring file writing for a particular output format, presenting an integer option.
     */
    class IntOption : public QgsVectorFileWriter::Option
    {
      public:
        IntOption( const QString &docString, int defaultValue )
          : Option( docString, Int )
          , defaultValue( defaultValue )
        {}

        int defaultValue;
    };

    /**
     * \ingroup core
     * \brief An available option for configuring file writing for a particular output format, presenting an boolean option.
     */
    class BoolOption : public QgsVectorFileWriter::SetOption
    {
      public:
        BoolOption( const QString &docString, bool defaultValue )
          : SetOption( docString, QStringList() << u"YES"_s << u"NO"_s, defaultValue ? "YES" : "NO" )
        {}
    };

    /**
     * \ingroup core
     * \brief A hidden option for file writing for a particular output format.
     */
    class HiddenOption : public QgsVectorFileWriter::Option
    {
      public:
        explicit HiddenOption( const QString &value )
          : Option( QString(), Hidden )
          , mValue( value )
        {}

        QString mValue;
    };

    struct MetaData
    {
        MetaData() = default;

        MetaData(
          const QString &longName,
          const QString &trLongName,
          const QString &glob,
          const QString &ext,
          const QMap<QString, QgsVectorFileWriter::Option *> &driverOptions,
          const QMap<QString, QgsVectorFileWriter::Option *> &layerOptions,
          const QString &compulsoryEncoding = QString()
        )
          : longName( longName )
          , trLongName( trLongName )
          , glob( glob )
          , ext( ext )
          , driverOptions( driverOptions )
          , layerOptions( layerOptions )
          , compulsoryEncoding( compulsoryEncoding )
        {}

        QString longName;
        QString trLongName;
        QString glob;
        QString ext;
        QMap<QString, QgsVectorFileWriter::Option *> driverOptions;
        QMap<QString, QgsVectorFileWriter::Option *> layerOptions;
        //! Some formats require a compulsory encoding, typically UTF-8. If no compulsory encoding, empty string
        QString compulsoryEncoding;
    };

    enum WriterError
    {
      NoError = 0,
      ErrDriverNotFound,
      ErrCreateDataSource,
      ErrCreateLayer,
      ErrAttributeTypeUnsupported,
      ErrAttributeCreationFailed,
      ErrProjection,
      ErrFeatureWriteFailed,
      ErrInvalidLayer,
      ErrSavingMetadata, //!< Metadata saving failed
      Canceled,          //!< Writing was interrupted by manual cancellation
    };

    /**
     * Source for exported field names.
     *
     * \since QGIS 3.18
     */
    enum FieldNameSource
    {
      Original = 0, //!< Use original field names
      PreferAlias,  //!< Use the field alias as the exported field name, wherever one is set. Otherwise use the original field names.
    };

    /**
     * Options for sorting and filtering vector formats.
     */
    enum VectorFormatOption SIP_ENUM_BASETYPE( IntFlag )
    {
      SortRecommended = 1 << 1,        //!< Use recommended sort order, with extremely commonly used formats listed first
      SkipNonSpatialFormats = 1 << 2,  //!< Filter out any formats which do not have spatial support (e.g. those which cannot save geometries)
      SupportsMultipleLayers = 1 << 3, //!< Filter to only formats which support multiple layers \since QGIS 3.32
    };
    Q_DECLARE_FLAGS( VectorFormatOptions, VectorFormatOption )

    /**
     * \ingroup core
     * \brief Interface to convert raw field values to their user-friendly values.
     */
    class CORE_EXPORT FieldValueConverter
    {
      public:
        FieldValueConverter() = default;

        virtual ~FieldValueConverter() = default;

        /**
         * Returns a possibly modified field definition. Default implementation will return provided field unmodified.
         * \param field original field definition
         * \returns possibly modified field definition
         */
        virtual QgsField fieldDefinition( const QgsField &field );

        /**
         * Convert the provided value, for field fieldIdxInLayer. Default implementation will return provided value unmodified.
         * \param fieldIdxInLayer field index
         * \param value original raw value
         * \returns possibly modified value.
         */
        virtual QVariant convert( int fieldIdxInLayer, const QVariant &value );

        /**
         * Creates a clone of the FieldValueConverter.
         */
        virtual QgsVectorFileWriter::FieldValueConverter *clone() const SIP_FACTORY;
    };

    /**
     * Edition capability flags
    */
    enum EditionCapability SIP_ENUM_BASETYPE( IntFlag )
    {
      //! Flag to indicate that a new layer can be added to the dataset
      CanAddNewLayer = 1 << 0,

      //! Flag to indicate that new features can be added to an existing layer
      CanAppendToExistingLayer = 1 << 1,

      //! Flag to indicate that new fields can be added to an existing layer. Imply CanAppendToExistingLayer
      CanAddNewFieldsToExistingLayer = 1 << 2,

      //! Flag to indicate that an existing layer can be deleted
      CanDeleteLayer = 1 << 3
    };

    /**
     * Combination of CanAddNewLayer, CanAppendToExistingLayer, CanAddNewFieldsToExistingLayer or CanDeleteLayer
    */
    Q_DECLARE_FLAGS( EditionCapabilities, EditionCapability )

    /**
     * Enumeration to describe how to handle existing files
     */
    enum ActionOnExistingFile
    {
      //! Create or overwrite file
      CreateOrOverwriteFile,

      //! Create or overwrite layer
      CreateOrOverwriteLayer,

      //! Append features to existing layer, but do not create new fields
      AppendToLayerNoNewFields,

      //! Append features to existing layer, and create new fields if needed
      AppendToLayerAddFields
    };

#ifndef SIP_RUN

    /**
     * Write contents of vector layer to an (OGR supported) vector format
     * \param layer layer to write
     * \param fileName file name to write to
     * \param fileEncoding encoding to use
     * \param destCRS CRS to reproject exported geometries to, or invalid CRS for no reprojection
     * \param driverName OGR driver to use
     * \param onlySelected write only selected features of layer
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param datasourceOptions list of OGR data source creation options
     * \param layerOptions list of OGR layer creation options
     * \param skipAttributeCreation only write geometries
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param symbologyExport symbology to export
     * \param symbologyScale scale of symbology
     * \param filterExtent if not NULLPTR, only features intersecting the extent will be saved (added in QGIS 2.4)
     * \param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
     * allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
     * \param forceMulti set to TRUE to force creation of multipart geometries (added in QGIS 2.14)
     * \param includeZ set to TRUE to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
     * \param attributes attributes to export (empty means all unless skipAttributeCreation is set)
     * \param fieldValueConverter field value converter (added in QGIS 2.16)
     * \param newLayer will be set to the new layer name created (in case it is different to the provided layer name) (added in QGIS 3.4, not available in python)
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#else

    /**
     * Write contents of vector layer to an (OGR supported) vector format
     * \param layer layer to write
     * \param fileName file name to write to
     * \param fileEncoding encoding to use
     * \param destCRS CRS to reproject exported geometries to, or invalid CRS for no reprojection
     * \param driverName OGR driver to use
     * \param onlySelected write only selected features of layer
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param datasourceOptions list of OGR data source creation options
     * \param layerOptions list of OGR layer creation options
     * \param skipAttributeCreation only write geometries
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param symbologyExport symbology to export
     * \param symbologyScale scale of symbology
     * \param filterExtent if not NULLPTR, only features intersecting the extent will be saved (added in QGIS 2.4)
     * \param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
     * allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
     * \param forceMulti set to TRUE to force creation of multipart geometries (added in QGIS 2.14)
     * \param includeZ set to TRUE to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
     * \param attributes attributes to export (empty means all unless skipAttributeCreation is set)
     * \param fieldValueConverter field value converter (added in QGIS 2.16)
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#endif
    Q_DECL_DEPRECATED static QgsVectorFileWriter::WriterError writeAsVectorFormat(
      QgsVectorLayer *layer,
      const QString &fileName,
      const QString &fileEncoding,
      const QgsCoordinateReferenceSystem &destCRS = QgsCoordinateReferenceSystem(),
      const QString &driverName = "GPKG",
      bool onlySelected = false,
      QString *errorMessage SIP_OUT = nullptr,
      const QStringList &datasourceOptions = QStringList(),
      const QStringList &layerOptions = QStringList(),
      bool skipAttributeCreation = false,
      QString *newFilename = nullptr,
      Qgis::FeatureSymbologyExport symbologyExport = Qgis::FeatureSymbologyExport::NoSymbology,
      double symbologyScale = 1.0,
      const QgsRectangle *filterExtent = nullptr,
      Qgis::WkbType overrideGeometryType = Qgis::WkbType::Unknown,
      bool forceMulti = false,
      bool includeZ = false,
      const QgsAttributeList &attributes = QgsAttributeList(),
      QgsVectorFileWriter::FieldValueConverter *fieldValueConverter = nullptr
#ifndef SIP_RUN
      ,
      QString *newLayer = nullptr
    );
#else
    ) SIP_DEPRECATED;
#endif

#ifndef SIP_RUN

    /**
     * Writes a layer out to a vector file.
     * \param layer layer to write
     * \param fileName file name to write to
     * \param fileEncoding encoding to use
     * \param ct coordinate transform to reproject exported geometries with, or invalid transform
     * for no transformation
     * \param driverName OGR driver to use
     * \param onlySelected write only selected features of layer
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param datasourceOptions list of OGR data source creation options
     * \param layerOptions list of OGR layer creation options
     * \param skipAttributeCreation only write geometries
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param symbologyExport symbology to export
     * \param symbologyScale scale of symbology
     * \param filterExtent if not NULLPTR, only features intersecting the extent will be saved (added in QGIS 2.4)
     * \param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
     * allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
     * \param forceMulti set to TRUE to force creation of multipart geometries (added in QGIS 2.14)
     * \param includeZ set to TRUE to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
     * \param attributes attributes to export (empty means all unless skipAttributeCreation is set)
     * \param fieldValueConverter field value converter (added in QGIS 2.16)
     * \param newLayer will be set to the new layer name created (in case it is different to the provided layer name) (added in QGIS 3.4, not available in python)
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#else

    /**
     * Writes a layer out to a vector file.
     * \param layer layer to write
     * \param fileName file name to write to
     * \param fileEncoding encoding to use
     * \param ct coordinate transform to reproject exported geometries with, or invalid transform
     * for no transformation
     * \param driverName OGR driver to use
     * \param onlySelected write only selected features of layer
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param datasourceOptions list of OGR data source creation options
     * \param layerOptions list of OGR layer creation options
     * \param skipAttributeCreation only write geometries
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param symbologyExport symbology to export
     * \param symbologyScale scale of symbology
     * \param filterExtent if not NULLPTR, only features intersecting the extent will be saved (added in QGIS 2.4)
     * \param overrideGeometryType set to a valid geometry type to override the default geometry type for the layer. This parameter
     * allows for conversion of geometryless tables to null geometries, etc (added in QGIS 2.14)
     * \param forceMulti set to TRUE to force creation of multipart geometries (added in QGIS 2.14)
     * \param includeZ set to TRUE to include z dimension in output. This option is only valid if overrideGeometryType is set. (added in QGIS 2.14)
     * \param attributes attributes to export (empty means all unless skipAttributeCreation is set)
     * \param fieldValueConverter field value converter (added in QGIS 2.16)
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#endif
    Q_DECL_DEPRECATED static QgsVectorFileWriter::WriterError writeAsVectorFormat(
      QgsVectorLayer *layer,
      const QString &fileName,
      const QString &fileEncoding,
      const QgsCoordinateTransform &ct,
      const QString &driverName = "GPKG",
      bool onlySelected = false,
      QString *errorMessage SIP_OUT = nullptr,
      const QStringList &datasourceOptions = QStringList(),
      const QStringList &layerOptions = QStringList(),
      bool skipAttributeCreation = false,
      QString *newFilename = nullptr,
      Qgis::FeatureSymbologyExport symbologyExport = Qgis::FeatureSymbologyExport::NoSymbology,
      double symbologyScale = 1.0,
      const QgsRectangle *filterExtent = nullptr,
      Qgis::WkbType overrideGeometryType = Qgis::WkbType::Unknown,
      bool forceMulti = false,
      bool includeZ = false,
      const QgsAttributeList &attributes = QgsAttributeList(),
      QgsVectorFileWriter::FieldValueConverter *fieldValueConverter = nullptr
#ifndef SIP_RUN
      ,
      QString *newLayer = nullptr
    );
#else
    ) SIP_DEPRECATED;
#endif

    /**
     * \ingroup core
     * \brief Options to pass to QgsVectorFileWriter::writeAsVectorFormat().
     */
    class CORE_EXPORT SaveVectorOptions
    {
      public:
        SaveVectorOptions();

        virtual ~SaveVectorOptions() = default;

        //! OGR driver to use
        QString driverName;

        //! Layer name. If let empty, it will be derived from the filename
        QString layerName;

        //! Action on existing file
        QgsVectorFileWriter::ActionOnExistingFile actionOnExistingFile = CreateOrOverwriteFile;

        //! Encoding to use
        QString fileEncoding;

        /**
         * Transform to reproject exported geometries with, or invalid transform
         * for no transformation
        */
        QgsCoordinateTransform ct;

        //! Write only selected features of layer
        bool onlySelectedFeatures = false;

        //! List of OGR data source creation options
        QStringList datasourceOptions;

        //! List of OGR layer creation options
        QStringList layerOptions;

        //! Only write geometries
        bool skipAttributeCreation = false;

        //! Attributes to export (empty means all unless skipAttributeCreation is set)
        QgsAttributeList attributes;

        //! Attributes export names
        QStringList attributesExportNames;

        //! Symbology to export
        Qgis::FeatureSymbologyExport symbologyExport = Qgis::FeatureSymbologyExport::NoSymbology;

        //! Scale of symbology
        double symbologyScale = 1.0;

        /**
         * If not empty, only features intersecting the extent will be saved.
         *
         * The filter extent should be in the destination CRS (if transforming), or the layer's
         * CRS if no valid transform is set.
         */
        QgsRectangle filterExtent;

        /**
         * Set to a valid geometry type to override the default geometry type for the layer. This parameter
         * allows for conversion of geometryless tables to null geometries, etc.
        */
        Qgis::WkbType overrideGeometryType = Qgis::WkbType::Unknown;

        //! Sets to TRUE to force creation of multipart geometries
        bool forceMulti = false;

        //! Sets to TRUE to include z dimension in output. This option is only valid if overrideGeometryType is set
        bool includeZ = false;

        /**
         * Field value converter.
         *
         * Ownership is not transferred and callers must ensure that the lifetime of fieldValueConverter
         * exceeds the lifetime of the QgsVectorFileWriter object.
         */
        QgsVectorFileWriter::FieldValueConverter *fieldValueConverter = nullptr;

        //! Optional feedback object allowing cancellation of layer save
        QgsFeedback *feedback = nullptr;

        /**
         * Source for exported field names.
         *
         * \since QGIS 3.18
         */
        QgsVectorFileWriter::FieldNameSource fieldNameSource = QgsVectorFileWriter::Original;

        /**
         * Set to TRUE to save layer metadata for the exported vector file.
         *
         * \see layerMetadata
         * \since QGIS 3.20
         */
        bool saveMetadata = false;

        /**
         * Layer metadata to save for the exported vector file. This will only be used if saveMetadata is TRUE.
         *
         * \see saveMetadata
         * \since QGIS 3.20
         */
        QgsLayerMetadata layerMetadata;

        /**
         * Set to TRUE to transfer field constraints to the exported vector file.
         *
         * Support for field constraints depends on the output file format.
         *
         * \since QGIS 3.34
         */
        bool includeConstraints = false;

        /**
         * Set to TRUE to transfer field domains to the exported vector file.
         *
         * Support for field domains depends on the output file format.
         *
         * \note Only available in builds based on GDAL 3.5 or later
         * \since QGIS 3.36
         */
        bool setFieldDomains = true;

        /**
         * Source database provider connection, for field domains.
         *
         * Ownership is not transferred and callers must ensure that the lifetime of sourceDatabaseProviderConnection
         * exceeds the lifetime of the QgsVectorFileWriter object.
         *
         * \since QGIS 3.36
         */
        const QgsAbstractDatabaseProviderConnection *sourceDatabaseProviderConnection = nullptr;
    };

#ifndef SIP_RUN

    /**
     * Writes a layer out to a vector file.
     * \param layer source layer to write
     * \param fileName file name to write to
     * \param options options.
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param newLayer will be set to the new layer name created (in case it is different to the provided layer name) (added in QGIS 3.4, not available in python)
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#else

    /**
     * Writes a layer out to a vector file.
     * \param layer source layer to write
     * \param fileName file name to write to
     * \param options options.
     * \param newFilename will be set to the new file name created (in case it is different to fileName).
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \returns QgsVectorFileWriter::WriterError::NoError in case of success, or error code in case of error
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
#endif
    Q_DECL_DEPRECATED static QgsVectorFileWriter::WriterError writeAsVectorFormat(
      QgsVectorLayer *layer,
      const QString &fileName,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QString *newFilename = nullptr,
      QString *errorMessage SIP_OUT = nullptr
#ifndef SIP_RUN
      ,
      QString *newLayer = nullptr
    );
#else
    ) SIP_DEPRECATED;
#endif

    /**
     * Create a new vector file writer
     * \deprecated QGIS 3.40. Use create() instead.
     */
    Q_DECL_DEPRECATED QgsVectorFileWriter(
      const QString &vectorFileName,
      const QString &fileEncoding,
      const QgsFields &fields,
      Qgis::WkbType geometryType,
      const QgsCoordinateReferenceSystem &srs = QgsCoordinateReferenceSystem(),
      const QString &driverName = "GPKG",
      const QStringList &datasourceOptions = QStringList(),
      const QStringList &layerOptions = QStringList(),
      QString *newFilename = nullptr,
      Qgis::FeatureSymbologyExport symbologyExport = Qgis::FeatureSymbologyExport::NoSymbology,
      QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags()
#ifndef SIP_RUN
        ,
      QString *newLayer = nullptr,
      const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext(),
      FieldNameSource fieldNameSource = Original
#endif
    ) SIP_DEPRECATED;

    /**
     * Create a new vector file writer.
     * \param vectorFileName file name to write to
     * \param fileEncoding encoding to use
     * \param fields fields to write
     * \param geometryType geometry type of output file
     * \param srs spatial reference system of output file
     * \param driverName OGR driver to use
     * \param datasourceOptions list of OGR data source creation options
     * \param layerOptions list of OGR layer creation options
     * \param newFilename potentially modified file name (output parameter)
     * \param symbologyExport symbology to export
     * \param fieldValueConverter field value converter (added in QGIS 2.16)
     * \param layerName layer name. If let empty, it will be derived from the filename (added in QGIS 3.0)
     * \param action action on existing file (added in QGIS 3.0)
     * \param newLayer potentially modified layer name (output parameter) (added in QGIS 3.4)
     * \param transformContext transform context, needed if the output file srs is forced to specific crs (added in QGIS 3.10.3)
     * \param sinkFlags feature sink flags (added in QGIS 3.10.3)
     * \param fieldNameSource source for field names (since QGIS 3.18)
     * \param includeConstraints set to TRUE to copy field constraints to the destination layer (since QGIS 3.34)
     * \param setFieldDomains set to TRUE to copy field domains (since QGIS 3.36)
     * \param sourceDatabaseProviderConnection source database provider connection, for field domains (since QGIS 3.36)
     * \note not available in Python bindings
     * \deprecated QGIS 3.40. Use create() instead.
     */
    Q_DECL_DEPRECATED QgsVectorFileWriter(
      const QString &vectorFileName,
      const QString &fileEncoding,
      const QgsFields &fields,
      Qgis::WkbType geometryType,
      const QgsCoordinateReferenceSystem &srs,
      const QString &driverName,
      const QStringList &datasourceOptions,
      const QStringList &layerOptions,
      QString *newFilename,
      Qgis::FeatureSymbologyExport symbologyExport,
      QgsVectorFileWriter::FieldValueConverter *fieldValueConverter,
      const QString &layerName,
      QgsVectorFileWriter::ActionOnExistingFile action,
      QString *newLayer = nullptr,
      const QgsCoordinateTransformContext &transformContext = QgsCoordinateTransformContext(),
      QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(),
      FieldNameSource fieldNameSource = Original,
      bool includeConstraints = false,
      bool setFieldDomains = true,
      const QgsAbstractDatabaseProviderConnection *sourceDatabaseProviderConnection = nullptr
    ) SIP_SKIP;

    QgsVectorFileWriter( const QgsVectorFileWriter &rh ) = delete;
    QgsVectorFileWriter &operator=( const QgsVectorFileWriter &rh ) = delete;

    /**
     * Create a new vector file writer.
     * \param fileName file name to write to
     * \param fields fields to write
     * \param geometryType geometry type of output file
     * \param srs spatial reference system of output file
     * \param transformContext coordinate transform context
     * \param options save options
     * \param sinkFlags feature sink flags
     * \param newFilename potentially modified file name (output parameter)
     * \param newLayer potentially modified layer name (output parameter)
     * \since QGIS 3.10.3
     */
    static QgsVectorFileWriter *create(
      const QString &fileName,
      const QgsFields &fields,
      Qgis::WkbType geometryType,
      const QgsCoordinateReferenceSystem &srs,
      const QgsCoordinateTransformContext &transformContext,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags(),
      QString *newFilename = nullptr,
      QString *newLayer = nullptr
    ) SIP_FACTORY;

    /**
     * Writes a layer out to a vector file.
     * \param layer source layer to write
     * \param fileName file name to write to
     * \param transformContext coordinate transform context
     * \param options save options
     * \param newFilename potentially modified file name (output parameter)
     * \param newLayer potentially modified layer name (output parameter)
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \returns Error message code, or QgsVectorFileWriter.NoError if the write operation was successful
     * \deprecated QGIS 3.20. Use writeAsVectorFormatV3() instead.
     */
    Q_DECL_DEPRECATED static QgsVectorFileWriter::WriterError writeAsVectorFormatV2(
      QgsVectorLayer *layer,
      const QString &fileName,
      const QgsCoordinateTransformContext &transformContext,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QString *newFilename = nullptr,
      QString *newLayer = nullptr,
      QString *errorMessage SIP_OUT = nullptr
    ) SIP_DEPRECATED;

    /**
     * Writes a layer out to a vector file.
     * \param layer source layer to write
     * \param fileName file name to write to
     * \param transformContext coordinate transform context
     * \param options save options
     * \param newFilename potentially modified file name (output parameter)
     * \param newLayer potentially modified layer name (output parameter)
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \returns Error message code, or QgsVectorFileWriter.NoError if the write operation was successful
     * \since QGIS 3.20
     */
    static QgsVectorFileWriter::WriterError writeAsVectorFormatV3(
      QgsVectorLayer *layer,
      const QString &fileName,
      const QgsCoordinateTransformContext &transformContext,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QString *errorMessage SIP_OUT = nullptr,
      QString *newFilename SIP_OUT = nullptr,
      QString *newLayer SIP_OUT = nullptr
    );

    /**
     * Details of available filters and formats.
     */
    struct FilterFormatDetails
    {
        //! Unique driver name
        QString driverName;

        //! Filter string for file picker dialogs
        QString filterString;

        /**
       * Matching glob patterns for format, e.g. ``*.shp``.
       * \since QGIS 3.2
       */
        QStringList globs;
    };

    /**
     * Returns a list or pairs, with format filter string as first element and OGR format key as second element.
     *
     * The \a options argument can be used to control the sorting and filtering of
     * returned formats.
     *
     * \see supportedFormatExtensions()
     */
    static QList< QgsVectorFileWriter::FilterFormatDetails > supportedFiltersAndFormats( VectorFormatOptions options = SortRecommended );

    /**
     * Returns a list of file extensions for supported formats, e.g "shp", "gpkg".
     *
     * The \a options argument can be used to control the sorting and filtering of
     * returned formats.
     *
     * \see supportedFiltersAndFormats()
     */
    static QStringList supportedFormatExtensions( VectorFormatOptions options = SortRecommended );

    /**
     * Returns TRUE if the specified \a driverName supports feature styles.
     *
     * The \a driverName argument must be a valid GDAL driver name.
     *
     */
    static bool supportsFeatureStyles( const QString &driverName );

    /**
     * Details of available driver formats.
     */
    struct DriverDetails
    {
        //! Descriptive, user friendly name for the driver
        QString longName;

        //! Unique driver name
        QString driverName;
    };

    /**
     * Returns the driver list that can be used for dialogs. It contains all OGR drivers
     * plus some additional internal QGIS driver names to distinguish between more
     * supported formats of the same OGR driver.
     *
     * The returned list consists of structs containing the driver long name (e.g. user-friendly
     * display name for the format) and internal driver short name.
     *
     * The \a options argument can be used to control the sorting and filtering of
     * returned drivers.
     */
    static QList< QgsVectorFileWriter::DriverDetails > ogrDriverList( VectorFormatOptions options = SortRecommended );

    /**
     * Returns the OGR driver name for a specified file \a extension. E.g. the
     * driver name for the ".shp" extension is "ESRI Shapefile".
     * If no suitable drivers are found then an empty string is returned.
     */
    static QString driverForExtension( const QString &extension );

    /**
     * Returns filter string that can be used for dialogs.
     *
     * The \a options argument can be used to control the sorting and filtering of
     * returned drivers.
     */
    static QString fileFilterString( VectorFormatOptions options = SortRecommended );

    //! Creates a filter for an OGR driver key
    static QString filterForDriver( const QString &driverName );

    //! Converts codec name to string passed to ENCODING layer creation option of OGR Shapefile
    static QString convertCodecNameForEncodingOption( const QString &codecName );

    //! Checks whether there were any errors in constructor
    QgsVectorFileWriter::WriterError hasError() const;

    //! Retrieves error message
    QString errorMessage() const;

    /**
     * Returns the GDAL (short) driver name associated with the output file.
     *
     * \see driverLongName()
     * \since QGIS 3.32
     */
    QString driver() const;

    /**
     * Returns the GDAL long driver name associated with the output file.
     *
     * \see driver()
     * \since QGIS 3.32
     */
    QString driverLongName() const;

    /**
     * Returns the capabilities supported by the writer.
     *
     * \since QGIS 3.32
     */
    Qgis::VectorFileWriterCapabilities capabilities() const;

    bool addFeature( QgsFeature &feature, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;

    using QgsFeatureSink::addFeatures;
    bool addFeatures( QgsFeatureList &features, QgsFeatureSink::Flags flags = QgsFeatureSink::Flags() ) override;
    QString lastError() const override;

    /**
     * Adds a \a feature to the currently opened data source, using the style from a specified \a renderer.
     */
    bool addFeatureWithStyle( QgsFeature &feature, QgsFeatureRenderer *renderer, Qgis::DistanceUnit outputUnit = Qgis::DistanceUnit::Meters );

    /**
     * Returns the map of original source field index to output file field index.
     *
     * \since QGIS 3.44
     */
    QMap<int, int> sourceFieldIndexToWriterFieldIndex() const { return mAttrIdxToProviderIdx; }

    //! Close opened shapefile for writing
    ~QgsVectorFileWriter() override;

    /**
     * Delete a shapefile (and its accompanying shx / dbf / prj / qix / qpj / cpg / sbn / sbx / idm / ind)
     * \param fileName /path/to/file.shp
     * \returns bool TRUE if the file was deleted successfully
     */
    static bool deleteShapeFile( const QString &fileName );

    /**
     * Returns the feature symbology export handling for the writer.
     *
     * \see setSymbologyExport()
     */
    Qgis::FeatureSymbologyExport symbologyExport() const { return mSymbologyExport; }

    /**
     * Sets the feature symbology export handling for the writer.
     *
     * \see symbologyExport()
     */
    void setSymbologyExport( Qgis::FeatureSymbologyExport symExport ) { mSymbologyExport = symExport; }

    /**
     * Returns the reference scale for output.
     * The  scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
     * \see setSymbologyScale()
     */
    double symbologyScale() const { return mSymbologyScale; }

    /**
     * Set reference \a scale for output.
     * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
     * \see symbologyScale()
     */
    void setSymbologyScale( double scale );

    static bool driverMetadata( const QString &driverName, MetaData &driverMetadata );

    /**
     * Returns a list of the default dataset options for a specified driver.
     * \param driverName name of OGR driver
     * \see defaultLayerOptions()
     */
    static QStringList defaultDatasetOptions( const QString &driverName );

    /**
     * Returns a list of the default layer options for a specified driver.
     * \param driverName name of OGR driver
     * \see defaultDatasetOptions()
     */
    static QStringList defaultLayerOptions( const QString &driverName );

    /**
     * Gets the ogr geometry type from an internal QGIS wkb type enum.
     *
     * Will drop M values and convert Z to 2.5D where required.
     * \note not available in Python bindings
     */
    static OGRwkbGeometryType ogrTypeFromWkbType( Qgis::WkbType type ) SIP_SKIP;

    /**
     * Returns edition capabilities for an existing dataset name.
     */
    static QgsVectorFileWriter::EditionCapabilities editionCapabilities( const QString &datasetName );

    /**
     * Returns whether the target layer already exists.
     */
    static bool targetLayerExists( const QString &datasetName, const QString &layerName );

    /**
     * Returns whether there are among the attributes specified some that do not exist yet in the layer
     */
    static bool areThereNewFieldsToCreate( const QString &datasetName, const QString &layerName, QgsVectorLayer *layer, const QgsAttributeList &attributes );

  protected:
    //! \note not available in Python bindings
    OGRGeometryH createEmptyGeometry( Qgis::WkbType wkbType ) SIP_SKIP;

    gdal::ogr_datasource_unique_ptr mDS;
    OGRLayerH mLayer = nullptr;
    OGRSpatialReferenceH mOgrRef = nullptr;

    QgsFields mFields;

    //! Contains error value if construction was not successful
    WriterError mError;
    QString mErrorMessage;

    QTextCodec *mCodec = nullptr;

    //! Geometry type which is being used
    Qgis::WkbType mWkbType;

    //! Map attribute indices to OGR provider field indexes such as they are *after* the OGR provider has been created
    // In particular for a GeoPackage file, a field with the FID column is always put in first position.
    QMap<int, int> mAttrIdxToProviderIdx;

    //! Map attribute indices to OGR layer field indexes such as they are on the current mLayer instance
    QMap<int, int> mAttrIdxToOgrLayerIdx;

    Qgis::FeatureSymbologyExport mSymbologyExport = Qgis::FeatureSymbologyExport::NoSymbology;

    QMap< QgsSymbolLayer *, QString > mSymbolLayerTable;

    //! Scale for symbology export (e.g. for symbols units in map units)
    double mSymbologyScale;

    QString mOgrDriverName;
    QString mOgrDriverLongName;

    //! Field value converter
    FieldValueConverter *mFieldValueConverter = nullptr;

    //! Whether to transfer field constraints to output
    bool mIncludeConstraints = false;

    //! Whether to set field domains to output
    bool mSetFieldDomains = true;

  private:
#ifdef SIP_RUN
    QgsVectorFileWriter( const QgsVectorFileWriter &rh );
#endif

    struct PreparedWriterDetails
    {
        std::unique_ptr< QgsFeatureRenderer > renderer;
        QgsCoordinateReferenceSystem sourceCrs;
        Qgis::WkbType sourceWkbType = Qgis::WkbType::Unknown;
        QgsFields sourceFields;
        QString providerType;
        long long featureCount = 0;
        QgsFeatureIds selectedFeatureIds;
        QString dataSourceUri;
        QString storageType;
        QgsFeatureIterator geometryTypeScanIterator;
        QgsExpressionContext expressionContext;
        QSet< int > fieldsToConvertToInt;
        QgsRenderContext renderContext;
        bool shallTransform = false;
        QgsCoordinateReferenceSystem outputCrs;
        Qgis::WkbType destWkbType = Qgis::WkbType::Unknown;
        QgsAttributeList attributes;
        QgsFields outputFields;
        QgsFeatureIterator sourceFeatureIterator;
        QgsGeometry filterRectGeometry;
        std::unique_ptr< QgsGeometryEngine > filterRectEngine;
        QVariantMap providerUriParams;
        std::unique_ptr< QgsAbstractDatabaseProviderConnection > sourceDatabaseProviderConnection;
    };

    /**
     * Prepares a write by populating a PreparedWriterDetails object.
     * This MUST be called in the main thread.
     */
    static QgsVectorFileWriter::WriterError prepareWriteAsVectorFormat( QgsVectorLayer *layer, const QgsVectorFileWriter::SaveVectorOptions &options, PreparedWriterDetails &details );

    /**
     * Writes a previously prepared PreparedWriterDetails \a details object.
     * This is safe to call in a background thread.
     * \param details writer details
     * \param fileName file name to write to
     * \param transformContext coordinate transform context
     * \param options save options
     * \param newFilename potentially modified file name (output parameter)
     * \param newLayer potentially modified layer name (output parameter)
     * \param errorMessage will be set to the error message text, if an error occurs while writing the layer
     * \param sinkFlags optional sink flags (since QGIS 3.40)
     * \returns Error message code, or QgsVectorFileWriter.NoError if the write operation was successful
     * \since QGIS 3.10.3
     */
    static QgsVectorFileWriter::WriterError writeAsVectorFormatV2(
      PreparedWriterDetails &details,
      const QString &fileName,
      const QgsCoordinateTransformContext &transformContext,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QString *newFilename = nullptr,
      QString *newLayer = nullptr,
      QString *errorMessage SIP_OUT = nullptr,
      QgsFeatureSink::SinkFlags sinkFlags = QgsFeatureSink::SinkFlags()
    );

    /**
     * Writes a previously prepared PreparedWriterDetails \a details object.
     * This is safe to call in a background thread.
     * \deprecated QGIS 3.40. Use writeAsVectorFormatV2() instead.
     */
    Q_DECL_DEPRECATED static QgsVectorFileWriter::WriterError writeAsVectorFormat(
      PreparedWriterDetails &details,
      const QString &fileName,
      const QgsVectorFileWriter::SaveVectorOptions &options,
      QString *newFilename = nullptr,
      QString *errorMessage SIP_OUT = nullptr,
      QString *newLayer = nullptr
    ) SIP_DEPRECATED;

    void init(
      QString vectorFileName,
      QString fileEncoding,
      const QgsFields &fields,
      Qgis::WkbType geometryType,
      QgsCoordinateReferenceSystem srs,
      const QString &driverName,
      QStringList datasourceOptions,
      QStringList layerOptions,
      QString *newFilename,
      QgsVectorFileWriter::FieldValueConverter *fieldValueConverter,
      const QString &layerName,
      QgsVectorFileWriter::ActionOnExistingFile action,
      QString *newLayer,
      QgsFeatureSink::SinkFlags sinkFlags,
      const QgsCoordinateTransformContext &transformContext,
      FieldNameSource fieldNameSource,
      const QgsAbstractDatabaseProviderConnection *sourceDatabaseProviderConnection
    );
    void resetMap( const QgsAttributeList &attributes );

    std::unique_ptr< QgsFeatureRenderer > mRenderer;
    QgsRenderContext mRenderContext;


    std::unique_ptr< QgsCoordinateTransform > mCoordinateTransform;

    bool mUsingTransaction = false;
    QSet< QMetaType::Type > mSupportedListSubTypes;

    Qgis::VectorFileWriterCapabilities mCapabilities;

    void createSymbolLayerTable( QgsVectorLayer *vl, const QgsCoordinateTransform &ct, OGRDataSourceH ds );
    gdal::ogr_feature_unique_ptr createFeature( const QgsFeature &feature );
    bool writeFeature( OGRLayerH layer, OGRFeatureH feature );

    //! Writes features considering symbol level order
    QgsVectorFileWriter::WriterError exportFeaturesSymbolLevels( const PreparedWriterDetails &details, QgsFeatureIterator &fit, const QgsCoordinateTransform &ct, QString *errorMessage = nullptr );
    double mmScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits );
    double mapUnitScaleFactor( double scale, Qgis::RenderUnit symbolUnits, Qgis::DistanceUnit mapUnits );

    void startRender( QgsFeatureRenderer *sourceRenderer, const QgsFields &fields );
    void stopRender();
    std::unique_ptr< QgsFeatureRenderer > createSymbologyRenderer( QgsFeatureRenderer *sourceRenderer ) const;
    //! Adds attributes needed for classification
    static void addRendererAttributes( QgsFeatureRenderer *renderer, QgsRenderContext &context, const QgsFields &fields, QgsAttributeList &attList );

    //! Concatenates a list of options using their default values
    static QStringList concatenateOptions( const QMap<QString, Option *> &options );

    friend class QgsVectorFileWriterTask;
    friend class TestQgsVectorFileWriter;
};

Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::EditionCapabilities )
Q_DECLARE_OPERATORS_FOR_FLAGS( QgsVectorFileWriter::VectorFormatOptions )

// clazy:excludeall=qstring-allocations

#endif
