/*
 *******************************************************************************
 *
 *  Copyright 2026 RIEGL Laser Measurement Systems
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *  SPDX-License-Identifier: Apache-2.0
 *
 *******************************************************************************
 */
/*!
 *******************************************************************************
 *
 * \file    queryInsert.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point insert query
 * \version 2015-10-14/AW: Initial version
 * \version 2017-11-24/AW: Constructors declared as "explicit" (#2825)
 * \version 2018-07-05/AW: Wrapper function bindBuffer() added
 * \version 2020-01-17/AW: Wrapper function bindMember() added (#3497)
 * \version 2020-02-27/AW: Add bindId() for "riegl.id" data buffers (#3576)
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_QUERYINSERT_HPP
#define RIEGL_RDB_POINTCLOUD_QUERYINSERT_HPP

//---< INCLUDES >---------------------------------------------------------------

#include <memory>
#include <string>
#include <cstdlib>
#include <cstdint>

#include "riegl/rdb/pointcloud/dataTypes.hpp"
#include "riegl/rdb/pointcloud/pointcloudData.hpp"

//---< NAMESPACE >--------------------------------------------------------------

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< CLASS QueryInsert >------------------------------------------------------
/*!
 * \brief Point insert query
 *
 * This class can be used to insert (new) points into the database.
 *
 * \see riegl::rdb::Pointcloud::insert()
 *
 * \note You either must delete the query object or call close()
 *       __before__ the parent Pointcloud instance is closed/deleted!
 */
class QueryInsert
{
public:
    /*!
     * \brief Default constructor
     *
     * Creates a null query - i.e. the query cannot be used to insert points.
     *
     * \see riegl::rdb::Pointcloud::insert()
     */
    explicit QueryInsert();

    /*!
     * \brief Constructor
     *
     * Creates a query prepared for inserting points.
     *
     * \note  You cannot create new QueryInsert objects this way,
     *        use riegl::rdb::Pointcloud::insert() instead.
     */
    explicit QueryInsert(riegl::rdb::PointcloudData *pointcloud);

    /*!
     * \brief Check if query is not null
     *
     * \see valid()
     */
    operator bool() const;

    /*!
     * \brief Check if query is not null
     *
     * A null query cannot be used to insert points.
     */
    bool valid() const;

    /*!
     * \brief Finish query
     *
     * Call this function when done with inserting points.
     */
    void close();

    /*!
     * \brief Load multi-pass insert settings from JSON string
     *
     * This function can be used to change settings for insert queries
     * in multi-pass insert mode (see 'pointInsertMode' in the database
     * creation settings). To be applied, the settings must be loaded
     * before the first call of next(). For settings that have not been
     * explicitly specified, the default value is used. The settings are
     * not saved in the database. When parsing the JSON string fails, an
     * exception is thrown.
     *
     * Supported settings:
     *
     *     - temporary_file_path (string):
     *       Path of the temporary file; if not specified, null or an
     *       empty string, then the database path with suffix ".temp"
     *       is used.
     *       Default: empty
     *
     *     - points_per_block (integer [1]):
     *       Inserted points are grouped into blocks with this number
     *       of points before they are processed further.
     *       Default: 1048576 (= 16 * 65536)
     *
     *     - initial_voxel_size (float [m]):
     *       Inserted points are grouped into voxels with this edge
     *       length in meters. The value must be a power of two of the
     *       resolution of the point coordinates (usually 0.00025 m).
     *       After all points have been inserted, voxels are combined
     *       or subdivided to achieve the desired maximum number of
     *       points per voxel set in the database creation settings
     *       (see 'chunkSize').
     *       Default: 2.048 m
     *
     * Example JSON string:
     *
     *     {
     *         "temporary_file_path": "",
     *         "points_per_block": 1048576,
     *         "initial_voxel_size": 2.048
     *     }
     *
     * \see riegl::rdb::pointcloud::CreateSettings::pointInsertMode
     * \see riegl::rdb::pointcloud::CreateSettings::chunkSize
     */
    void loadMultiPassSettings(const std::string &json);

    /*!
     * \brief Bind attribute buffer
     *
     * Use this function to define a source buffer for a point attribute.
     * Exactly one buffer can be defined for an attribute (i.e. only the
     * most recently defined buffer will be used).
     *
     * You can but don't need to define a buffer for each attribute. If
     * no buffer is defined for an attribute, the attribute's default
     * value will be used instead.
     *
     * The buffer is expected to be n*s*d bytes large, where
     * __n__ is the number of points defined in next(),
     * __s__ is the size of one element as defined by 'dataType' and
     * __d__ is the number of attribute dimensions (elements).
     *
     * \note This function just stores the buffer pointer - it does
     *       __NOT__ copy the data contained in the buffer. So make
     *       sure that the buffer remains valid until you call next().
     *
     * \see riegl::rdb::pointcloud::PointAttributes
     */
    void bind(
        const std::string &attribute, //!< [in] attribute name
        const DataType     dataType,  //!< [in] buffer data type
        const void        *buffer,    //!< [in] buffer location
        const std::int32_t stride = 0 //!< [in] bytes between beginnings of successive elements (0: auto)
    );

    /*!
     * \brief Bind point id buffer
     *
     * If you provide a buffer for the point identifier attribute ("riegl.id"),
     * then it will receive the identifiers of the inserted points (PID). Each
     * point is assigned a unique PID on insertion. The PID starts at 1 for the
     * first point and is incremented by 1 for each subsequent point (so that
     * the PID reflects the insertion order of the points, but only as long as
     * riegl::rdb::pointcloud::CreateSettings::optimizePointID is set to false).
     *
     * \see bind()
     */
    void bindId(
        const DataType     dataType,  //!< [in] buffer data type
        void              *buffer,    //!< [in] buffer location
        const std::int32_t stride = 0 //!< [in] bytes between beginnings of successive elements (0: auto)
    );

    //! \copydoc bind()
    template <typename ValueType>
    void bindBuffer(
        const std::string &attribute, //!< [in] attribute name
        const ValueType   &buffer,    //!< [in] buffer (data, pointer to data, std::array or std::vector)
        const std::int32_t stride = 0 //!< [in] bytes between beginnings of successive elements (0: auto)
    )
    {
        bind(attribute, dataTypeOf(buffer), dataPointerOf(buffer), stride);
    }

    /*!
     * \copydoc bindId()
     * \see bindBuffer()
     */
    template <typename ValueType>
    void bindIdBuffer(
        ValueType         &buffer,    //!< [in] buffer (data, pointer to data, std::array or std::vector)
        const std::int32_t stride = 0 //!< [in] bytes between beginnings of successive elements (0: auto)
    )
    {
        bindId(dataTypeOf(buffer), dataPointerOf(buffer), stride);
    }

    /*!
     * \copydoc bindId()
     * \see bindBuffer()
     */
    template <typename ValueType>
    void bindIdBuffer(
        ValueType         *buffer,    //!< [in] buffer (data, pointer to data, std::array or std::vector)
        const std::int32_t stride = 0 //!< [in] bytes between beginnings of successive elements (0: auto)
    )
    {
        bindId(dataTypeOf(buffer), buffer, stride);
    }

    /*!
     * \brief Bind attribute buffer
     *
     * This is a variant of bindBuffer() that allows to bind a member variable
     * of an object as attribute buffer. The object can be part of a container
     * that stores the objects contiguously (e.g. std::vector, std::array) and
     * the stride is automatically derived from the object size.
     *
     * \see bindBuffer()
     */
    template <typename ObjectType, typename MemberPointer>
    void bindMember(
        const std::string  &attribute, //!< [in] attribute name
        const ObjectType   &object,    //!< [in] e.g. first object of container
        const MemberPointer member     //!< [in] object member variable pointer
    )
    {
        bindBuffer(
            attribute, object.*member,
            static_cast<std::int32_t>(sizeof(ObjectType))
        );
    }

    /*!
     * \copydoc bindId()
     * \see bindMember()
     */
    template <typename ObjectType, typename MemberPointer>
    void bindIdMember(
        ObjectType   &object, //!< [in] e.g. first object of container
        MemberPointer member  //!< [in] object member variable pointer
    )
    {
        bindIdBuffer(
            object.*member,
            static_cast<std::int32_t>(sizeof(ObjectType))
        );
    }

    //! \copydoc bindMember()
    template <typename ObjectType, typename MemberPointer>
    void bindMember(
        const std::string  &attribute, //!< [in] attribute name
        const ObjectType   &object,    //!< [in] e.g. first object of container
        const MemberPointer member,    //!< [in] object member variable pointer
        const std::size_t   index      //!< [in] index for array-like object members
    )
    {
        bindBuffer(
            attribute, (object.*member)[index],
            static_cast<std::int32_t>(sizeof(ObjectType))
        );
    }

    /*!
     * \copydoc bindId()
     * \see bindMember()
     */
    template <typename ObjectType, typename MemberPointer>
    void bindIdMember(
        ObjectType       &object, //!< [in] e.g. first object of container
        MemberPointer     member, //!< [in] object member variable pointer
        const std::size_t index   //!< [in] index for array-like object members
    )
    {
        bindIdBuffer(
            (object.*member)[index],
            static_cast<std::int32_t>(sizeof(ObjectType))
        );
    }

    /*!
     * \brief Insert points
     *
     * Use this function to actually read the point attributes from
     * all defined buffers and insert the points into the database.
     *
     * Afterwards you may re-fill the buffers or define new buffers
     * with bind() and call next() again until all points have been
     * inserted.
     *
     * \note IEEE-754 "NaN" values contained in floating point source
     *       buffers are ignored and the attribute's default value is
     *       used instead. Furthermore IEEE-754 "Infinity" values will
     *       always cause next() to fail with error code 10414, i.e.
     *       riegl::rdb::Error::QueryAttributeValueOutOfRange.
     *
     * \note Since version 2.4.2, unit vector point attributes (e.g.
     *       "riegl.direction") are automatically normalized (divided
     *       by the vector length) on insertion. This is done for all
     *       point attributes that meet the following requirements:
     *       `length` is 3, `minimumValue` is -1, `maximumValue` is 1,
     *       `resolution` is not 1, `scaleFactor` is 1, `unitSymbol` is
     *       empty, `invalidValue` is undefined and `tags` contains the
     *       "direction" tag but not the "do-not-normalize" tag.
     *
     * \returns the number of points inserted
     */
    std::uint32_t next(
        std::uint32_t count //!< [in] size of source buffers in terms of points
    );

private:
    struct Private;
    std::shared_ptr<Private> data;
};

}}} // namespace riegl::rdb::pointcloud

#endif // RIEGL_RDB_POINTCLOUD_QUERYINSERT_HPP
