/*
 *******************************************************************************
 *
 *  Copyright 2025 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    queryStat.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point statistics query
 * \version 2015-10-14/AW: Initial version
 * \version 2016-11-07/AW: Optionally filter index graph nodes (#2390)
 * \version 2017-11-24/AW: Constructors declared as "explicit" (#2825)
 * \version 2018-07-05/AW: Wrappers for minimum() and maximum() added
 * \version 2019-11-05/AW: Optionally return cleaned attribute extents (#3071)
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_QUERYSTAT_HPP
#define RIEGL_RDB_POINTCLOUD_QUERYSTAT_HPP

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

#include <memory>
#include <string>
#include <vector>
#include <cstdint>
#include "riegl/rdb/pointcloud/dataTypes.hpp"
#include "riegl/rdb/pointcloud/graphNode.hpp"
#include "riegl/rdb/pointcloud/transaction.hpp"
#include "riegl/rdb/pointcloud/pointcloudData.hpp"

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

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< CLASS QueryStat >--------------------------------------------------------
/*!
 * \brief Point statistics query
 *
 * This query provides point attribute statistics like minimum and
 * maximum value.
 *
 * \see riegl::rdb::Pointcloud::stat()
 *
 * \note You either must delete the query object or call close()
 *       __before__ the parent Pointcloud instance is closed/deleted!
 */
class QueryStat
{
public:
    /*!
     * \brief Default constructor
     *
     * Creates a null query - i.e. the query cannot be used to read stats.
     *
     * \see riegl::rdb::Pointcloud::stat()
     */
    explicit QueryStat();

    /*!
     * \brief Constructor
     *
     * Creates a query prepared for reading stats.
     *
     * \note  You cannot create new QueryStat objects this way,
     *        use riegl::rdb::Pointcloud::stat() instead.
     */
    explicit QueryStat(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 read stats.
     */
    bool valid() const;

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

    /*!
     * \brief Get index graph
     *
     * This function returns a simple directed graph which represents
     * the index structure that is used to organize the point cloud.
     *
     * The optional filter expression can be used to select particular
     * graph nodes - if no filter is given, all nodes will be returned.
     * Filter expression syntax see riegl::rdb::Pointcloud::select().
     *
     * Note: The reported point counts and attribute extents are not
     * affected by the filter expressions given here or defined in the
     * meta data item riegl.stored_filters.
     *
     * Details see description of class GraphNode.
     */
    void index(
        GraphNode         &root,                  //!< [out] graph root node
        const std::string &filter = std::string() //!< [in] optional filter expression
    );
    GraphNode index(
        const std::string &filter = std::string() //!< [in] optional filter expression
    )
    {
        GraphNode result;
        index(result, filter);
        return result;
    }

    /*!
     * \brief Lowest value of node
     *
     * Provides the attribute's minimum value of a branch (i.e. node and children).
     * If the node ID is zero, then the minimum value of all points is returned.
     *
     * The target buffer is expected to be s*d bytes large, where
     * __s__ is the size of one element as defined by 'dataType' and
     * __d__ is the number of attribute dimensions (elements).
     */
    void minimum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        const DataType       dataType,     //!< [in] data type of target buffer
        void                *buffer,       //!< [out] buffer for minimum value
        const bool           cleaned=false //!< [in] true: ignore invalid values
    );

    //! \copydoc minimum()
    template <typename ValueType>
    void minimum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        ValueType           &buffer,       //!< [out] buffer (data, pointer to data, std::array or std::vector)
        const bool           cleaned=false //!< [in] true: ignore invalid values
    )
    {
        minimum(nodeID, attribute, dataTypeOf(buffer), dataPointerOf(buffer), cleaned);
    }

    //! \copydoc minimum()
    template <typename ValueType>
    void minimum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        ValueType           *buffer,       //!< [out] buffer (data, pointer to data, std::array or std::vector)
        const bool           cleaned=false //!< [in] true: ignore invalid values
    )
    {
        minimum(nodeID, attribute, dataTypeOf(buffer), buffer, cleaned);
    }

    /*!
     * \brief Highest value of node
     *
     * Provides the attribute's maximum value of a branch (i.e. node and children).
     * If the node ID is zero, then the maximum value of all points is returned.
     *
     * The target buffer is expected to be s*d bytes large, where
     * __s__ is the size of one element as defined by 'dataType' and
     * __d__ is the number of attribute dimensions (elements).
     */
    void maximum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        const DataType       dataType,     //!< [in] data type of target buffer
        void                *buffer,       //!< [out] buffer for maximum value
        const bool           cleaned=false //!< [in] true: ignore invalid values
    );

    //! \copydoc maximum()
    template <typename ValueType>
    void maximum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        ValueType           &buffer,       //!< [out] buffer (data, pointer to data, std::array or std::vector)
        const bool           cleaned=false //!< [in] true: ignore invalid values
    )
    {
        maximum(nodeID, attribute, dataTypeOf(buffer), dataPointerOf(buffer), cleaned);
    }

    //! \copydoc maximum()
    template <typename ValueType>
    void maximum(
        const GraphNode::ID &nodeID,       //!< [in] node identifier
        const std::string   &attribute,    //!< [in] attribute name
        ValueType           *buffer,       //!< [out] buffer (data, pointer to data, std::array or std::vector)
        const bool           cleaned=false //!< [in] true: ignore invalid values
    )
    {
        maximum(nodeID, attribute, dataTypeOf(buffer), buffer, cleaned);
    }

    /*!
     * \brief Attribute revision
     *
     * Provides the ID of the last transaction that has modified the attribute
     * in any node of the given branch.
     */
    void revision(
        const GraphNode::ID &nodeID,    //!< [in] node identifier
        const std::string   &attribute, //!< [in] attribute name
        Transaction::ID     &revision   //!< [out] transaction ID, see above
    );
    Transaction::ID revision(
        const GraphNode::ID &nodeID,   //!< [in] node identifier
        const std::string   &attribute //!< [in] attribute name
    )
    {
        Transaction::ID result;
        revision(nodeID, attribute, result);
        return result;
    }

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

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

#endif // RIEGL_RDB_POINTCLOUD_QUERYSTAT_HPP
