/*
 *******************************************************************************
 *
 *  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    pointAttributes.h
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Manage point attributes
 * \version 2015-10-14/AW: Initial version
 * \version 2016-01-04/AW: Added function to duplicate point attribute data
 * \version 2016-01-11/AW: Added function to discard point attribute data
 * \version 2016-11-29/AW: Added function to define a RIEGL point attribute
 * \version 2017-03-23/AW: Added function to modify point attribute details
 * \version 2017-11-09/AW: Added function to merge point attribute details
 * \version 2019-04-15/AW: Added function to query point attribute group (#3342)
 * \version 2020-12-02/AW: put() accepts name, title, description & unit (#3750)
 * \version 2022-09-20/AW: Added function to query attributes of filter (#4349)
 *
 * This class allows to manage point attributes (dimensions).
 *
 * When creating a new database, each point has at least two attributes:
 *
 *  - an unique point identifier (ID)
 *  - the primary attribute specified in CreateSettings::primaryAttribute
 *
 * Both attributes cannot be deleted or modified in any way.
 *
 * The point identifier attribute is always named "id" and is an unsigned 64
 * bit integer number (see DataTypes::UINT64, pointIDType() and pointIDName()).
 *
 * \see riegl::rdb::Pointcloud::pointAttribute()
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_POINTATTRIBUTES_H
#define RIEGL_RDB_POINTCLOUD_POINTATTRIBUTES_H

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

#include "riegl/rdb.h"

//---< FUNCTIONS >--------------------------------------------------------------

RDB_LIBRARY_API_BEGIN

typedef uint64_t rdb_pointcloud_point_attributes_point_id;

//______________________________________________________________________________
/*!
 * \brief Query attribute names
 *
 * This function returns the names of the attributes defined in the
 * database in the order defined by the point attribute group table
 * (details see group() function).
 *
 * \see rdb_pointcloud_point_attributes_group()
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_list,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    uint32_t      *count,      //!< [out] number of names
    RDBString     *list        //!< [out] list of names, separated by \0
);

//______________________________________________________________________________
/*!
 * \brief Query default attribute names
 * \returns a list of built-in RIEGL default attribute names
 * \note the order of attribute names is undefined
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_list_default,
    RDBContext *context, //!< [in] library context
    uint32_t   *count,   //!< [out] number of names
    RDBString  *list     //!< [out] list of names, separated by \0
);

//______________________________________________________________________________
/*!
 * \brief Query attribute names of filter
 * \returns a list of attribute names used in the filter string
 *
 * __Examples:__
 *
 *     filter: (riegl.reflectance > 0)
 *     result: [riegl.reflectance]
 *
 *     filter: (riegl.id < 100) && (riegl.class == 2)
 *     result: [riegl.id, riegl.class]
 *
 *     filter: (riegl.target_index == riegl.target_count)
 *     result: [riegl.target_index, riegl.target_count]
 *
 *     filter: (riegl.xyz[0] > 0.0) && (riegl.xyz[1] > 0.0)
 *     result: [riegl.xyz]
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_list_filtered,
    RDBContext *context, //!< [in] library context
    RDBString   filter,  //!< [in] filter string
    uint32_t   *count,   //!< [out] number of names
    RDBString  *list     //!< [out] list of names, separated by \0
);

//______________________________________________________________________________
/*!
 * \brief Check if attribute exists
 * \returns true if an attribute with given name exists
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_exists,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      name,       //!< [in] point attribute name
    uint32_t      *exists      //!< [out] 0 if attribute does not exist
);

//______________________________________________________________________________
/*!
 * \brief Add new attribute
 *
 * \note All attribute name related functions are case sensitive
 *       (i.e. "xyz" and "XYZ" are different attributes).
 * \note Attribute names are unique. If an attribute with the same
 *       name already exists in the database, this function fails.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_add,
    RDBContext                  *context,    //!< [in] library context
    RDBPointcloud               *pointcloud, //!< [in] point cloud handle
    RDBPointcloudPointAttribute *attribute   //!< [in] attribute information to be added
);

//______________________________________________________________________________
/*!
 * \brief Add RIEGL attribute
 *
 * If the given name refers to an built-in RIEGL default point attribute,
 * this function adds the point attribute to the database. Otherwise this
 * function fails.
 *
 * \note All attribute name related functions are case sensitive
 *       (i.e. "xyz" and "XYZ" are different attributes).
 * \note Attribute names are unique. If an attribute with the same
 *       name already exists in the database, this function fails.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_add_default,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      name        //!< [in] name of a RIEGL point attribute
);

//______________________________________________________________________________
/*!
 * \brief Query attribute details
 *
 * If the given attribute name could not be found, the function fails.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_get,
    RDBContext                  *context,    //!< [in] library context
    RDBPointcloud               *pointcloud, //!< [in] point cloud handle
    RDBString                    name,       //!< [in] point attribute name
    RDBPointcloudPointAttribute *attribute   //!< [out] attribute information (copy)
);

//______________________________________________________________________________
/*!
 * \brief Query attribute group and index
 *
 * Since RDB version 2.2 the point attributes are grouped and sorted for
 * a better overview. The grouping and order is defined by a table which
 * is stored in the meta data entry "riegl.point_attribute_groups". Each
 * database is filled with a default table with RIEGL point attributes.
 *
 * The list() function returns the names of the attributes actually used
 * in the order defined by the table. Function group() returns the group
 * name as well as the table index of a point attribute (index starts at
 * 1 for the first attribute in the first group and goes up to N for the
 * last attribute in the last group).
 *
 * So unless you want to change the order of the point attributes or add
 * your own, it is not necessary to access or change the attribute table.
 *
 * \see rdb_pointcloud_point_attributes_list()
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_group,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      name,       //!< [in] attribute name
    RDBString     *group,      //!< [out] attribute group name
    uint32_t      *index       //!< [out] attribute index
);

//______________________________________________________________________________
/*!
 * \brief Modify attribute details
 *
 * This function allows to modify certain point attribute properties.
 * Thereto the function looks for a point attribute of the given name,
 * compares the given properties with those stored in the database and
 * finally updates the modified properties in the database.
 *
 * \note Only the modification of `name`, `title`, `description`, `tags`,
 *       `unitSymbol`, `scaleFactor` and `lodSettings` is supported.
 *       If a point attribute shall be renamed, the `name` property
 *       must contain the old and the new name separated by " -> ".
 *       Example: To rename `riegl.xyz` to `riegl.xyz_socs`, set
 *       `attribute.name` to `riegl.xyz -> riegl.xyz_socs`.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_put,
    RDBContext                  *context,    //!< [in] library context
    RDBPointcloud               *pointcloud, //!< [in] point cloud handle
    RDBPointcloudPointAttribute *attribute   //!< [in] attribute information to be modified
);

//______________________________________________________________________________
/*!
 * \brief Query default attribute details
 *
 * This function is similar to get() but instead of returning the attribute
 * details defined in the database, it returns the details of the built-in
 * RIEGL default point attribute.
 *
 * If the given attribute name could not be found, the function fails.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_get_default,
    RDBContext                  *context,  //!< [in] library context
    RDBString                    name,     //!< [in] point attribute name
    RDBPointcloudPointAttribute *attribute //!< [out] attribute information (copy)
);

//______________________________________________________________________________
/*!
 * \brief Query default attribute group and index
 *
 * This function works like rdb_pointcloud_point_attributes_group() but returns
 * the group and index from the built-in RIEGL default attribute table.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_group_default,
    RDBContext *context, //!< [in] library context
    RDBString   name,    //!< [in] attribute name
    RDBString  *group,   //!< [out] attribute group name
    uint32_t   *index    //!< [out] attribute index
);

//______________________________________________________________________________
/*!
 * \brief Merge attribute details
 *
 * This function returns the details (description) of a point attribute by
 * analyzing two or more point clouds. If the attribute details are not the
 * same in all point clouds (e.g. a typo was fixed or the minimum, maximum,
 * resolution or default value has changed), merged details that cover all
 * variants are returned. However, some changes can not be merged, e.g.
 * changes to the vector length, the unit symbol or changes to the minimum,
 * maximum and resolution values that would require unsupported data types
 * (see riegl::rdb::pointcloud::DataType). In this case an exception with
 * error code riegl::rdb::Error::PointAttributeNotMergeable is thrown.
 * It is not required that the point attribute exists in all point clouds.
 *
 * This function might be helpful when merging point clouds, i.e. when
 * reading points from multiple databases and storing them in a single
 * database. In this case one needs to define the point attributes in the
 * target database. If the source databases were generated by different
 * software applications (or different versions of the same application),
 * the details of the same attribute may slightly differ (see above), which
 * makes the definition of the attribute in the target database quite hard.
 * So instead of defining the attributes by using a hard-coded table, one
 * can use this function to generate the attribute description at runtime.
 *
 * __Examples (simplified, fake attributes):__
 *
 *   | Example 1      | Attribute   | Description      | Resolution | Minimum | Maximum |
 *   | -------------- | ----------- | ---------------- |----------: | ------: | ------: |
 *   | Point cloud A  | amplitude   | signal amplitude |       0.01 |    0.00 |  655.35 |
 *   | Point cloud B  | amplitude   | signal amplitude |       0.01 | -327.68 |  327.67 |
 *   | Merged details | amplitude   | signal amplitude |       0.01 | -327.68 |  655.35 |
 *
 *   | Example 2      | Attribute   | Description         | Resolution | Minimum | Maximum |
 *   | -------------- | ----------- | ------------------- | ---------: | ------: | ------: |
 *   | Point cloud A  | temperature | coarse temperature  |       0.50 | -100.00 |  500.00 |
 *   | Point cloud B  | temperature | precise temperature |       0.01 | -100.00 | 1000.00 |
 *   | Merged details | temperature | precise temperature |       0.01 | -100.00 | 1000.00 |
 *
 * In both examples the value range (minimum/maximum) of the attribute has
 * changed, so the result range is the combination (disjunction) of both
 * ranges.
 *
 * In the second example the resolution is different too. The example
 * shows that always the higher resolution (lower value) is returned.
 *
 * If the description text changes, then the result is the text that is
 * stored in the younger database (the database that was created last).
 * In example 2, "Point cloud B" is the younger database.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_get_merged,
    RDBContext                  *context,     //!< [in] library context
    RDBPointcloud              **pointclouds, //!< [in] point cloud handles
    uint32_t                     count,       //!< [in] number of point clouds
    RDBString                    name,        //!< [in] point attribute name
    RDBPointcloudPointAttribute *attribute    //!< [out] attribute information (copy)
);

//______________________________________________________________________________
/*!
 * \brief Merge attribute details
 *
 * Same as rdb_pointcloud_point_attributes_get_merged(), but instead of
 * merging one point attribute, this function merges all point attributes
 * of all given point clouds.
 *
 * To query the number of point attributes, call with 'attribute_list'
 * and 'attribute_size' set to 0 and read 'attribute_size' afterwards.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_get_merged_all,
    RDBContext                   *context,         //!< [in] library context
    RDBPointcloud               **pointcloud_list, //!< [in] point cloud handles
    uint32_t                      pointcloud_size, //!< [in] number of point clouds
    RDBPointcloudPointAttribute **attribute_list,  //!< [out] attribute information array
    uint32_t                     *attribute_size   //!< [out] number of attributes
);

//______________________________________________________________________________
/*!
 * \brief Delete attribute
 *
 * If the given attribute name could not be found, this function does not fail.
 *
 * \note The primary point attribute (see CreateSettings::primaryAttribute)
 *       and the unique point identifier (ID) cannot be deleted.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_remove,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      name        //!< [in] point attribute name
);

//______________________________________________________________________________
/*!
 * \brief Duplicate attribute data
 *
 * Use this function to duplicate the current point attribute *data* (but
 * not the point attribute description). This can e.g. be used to make a
 * backup of point attribute data.
 *
 * \note The source and target attributes must exist and be compatible (i.e.
 *       attribute minimum, maximum, resolution and length are identical).
 *
 * \note The primary point attribute and the point ID attribute cannot be
 *       used as target attribute.
*/
RDB_FUNCTION(rdb_pointcloud_point_attributes_duplicate,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      source,     //!< [in] source attribute name
    RDBString      target      //!< [in] target attribute name
);

//______________________________________________________________________________
/*!
 * \brief Discard attribute data
 *
 * Use this function to delete the current point attribute *data* (but
 * not the point attribute description). This is equivalent to a newly
 * created point attribute, i.e. all points will have the default value.
 *
 * \note The primary point attribute (see CreateSettings::primaryAttribute)
 *       and the unique point identifier (ID) data cannot be discarded.
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_discard,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString      name        //!< [in] attribute name
);

//______________________________________________________________________________
/*!
 * \brief Point identifier attribute name
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_point_id_name,
    RDBString *value //!< [out] point attribute name
);

//______________________________________________________________________________
/*!
 * \brief Point identifier attribute unit
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_point_id_unit,
    RDBString *value //!< [out] point attribute unit
);

//______________________________________________________________________________
/*!
 * \brief Point identifier data type enum
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_point_id_type,
    uint32_t *value //!< [out] point attribute type enum
);

//______________________________________________________________________________
/*!
 * \brief Name of primary point attribute
 */
RDB_FUNCTION(rdb_pointcloud_point_attributes_primary_attribute_name,
    RDBContext    *context,    //!< [in] library context
    RDBPointcloud *pointcloud, //!< [in] point cloud handle
    RDBString     *value       //!< [out] point attribute name
);

RDB_LIBRARY_API_END

#endif // RIEGL_RDB_POINTCLOUD_POINTATTRIBUTES_H
