/*
 *******************************************************************************
 *
 *  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    queryInvert.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point invert query
 * \version 2016-11-14/AW: Initial version
 * \version 2017-11-24/AW: Constructors declared as "explicit" (#2825)
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_QUERYINVERT_HPP
#define RIEGL_RDB_POINTCLOUD_QUERYINVERT_HPP

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

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

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

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

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< CLASS QueryInvert >------------------------------------------------------
/*!
 * \brief Point invert query
 *
 * This class can be used to invert point attributes for all points or just
 * those that meet some filter criteria. Calculating the inverted value (v')
 * from the original value (v) and the attribute limits (min and max) is done
 * as follows:
 *
 *   v' = max - (v - min)
 *
 * For dynamic point attributes (bits/flags) this simply means to flip the
 * bit (e.g. set "riegl.selected" to 1 if it was 0, otherwise set it to 0).
 *
 * This query is similar to using select() and update() and manually inverting
 * the point attributes except that it might be faster (less processing time)
 * and easier to use.
 *
 * \see riegl::rdb::Pointcloud::invert()
 *
 * \note You either must delete the query object or call close()
 *       __before__ the parent Pointcloud instance is closed/deleted!
 */
class QueryInvert
{
public:
    /*!
     * \brief Default constructor
     *
     * Creates a null query - i.e. the query cannot be used to modify points.
     *
     * \see riegl::rdb::Pointcloud::invert()
     */
    explicit QueryInvert();

    /*!
     * \brief Constructor
     *
     * Creates a query prepared for modifying points.
     *
     * \note You cannot create new QueryInvert objects this way,
     *       use riegl::rdb::Pointcloud::invert() instead.
     */
    explicit QueryInvert(
        riegl::rdb::PointcloudData       *pointcloud,
        const std::vector<GraphNode::ID> *nodes,
        const std::string                &filter
    );

    /*!
     * \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 read points.
     */
    bool valid() const;

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

    /*!
     * \brief Define attribute
     *
     * Use this function to define the point attribute to be inverted. To
     * define multiple attributes, simply call this function once for each.
     *
     * \note Point attributes having a length greater than 1 (vectors like
     *       the true color attribute "riegl.rgba") can only be inverted
     *       as a whole, i.e. you cannot invert particular vector elements
     *       (e.g. using "riegl.rgba[0]" will not work).
     *
     * \see riegl::rdb::pointcloud::PointAttributes
     */
    void attribute(
        const std::string &name //!< [in] attribute name
    );

    /*!
     * \brief Invert points
     *
     * Use this function to actually invert the point attribute(s).
     *
     * To process all points, you need to repeatedly call next() until it
     * returns 0 (zero, see example 8). The number of points to process in
     * one step is defined by 'count'. Please note that the actual number
     * of processed points may be slightly different. To cancel processing
     * just stop calling next() and cancel (rollback) the transaction.
     *
     * \returns the number of points processed
     */
    std::uint32_t next(
        std::uint32_t count //!< [in] number of points to process
    );

    /*!
     * \brief Progress
     *
     * This function returns a coarse progress information in percent (0..100%).
     * Since the total number of modified points is not known in advance, this
     * value just reflects the progress of the (internal) index traversal.
     */
    std::uint32_t progress() const;

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

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

#endif // RIEGL_RDB_POINTCLOUD_QUERYINVERT_HPP
