{*
 *******************************************************************************
 *
 *  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    riegl.rdb.pointcloud.pointAttribute.pas
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point attribute description (Pascal wrapper code)
 * \version 2015-10-14/AW: Initial version
 * \version 2015-11-28/AW: Compression options added (#2423)
 * \version 2016-12-20/AW: New functions to load/save settings from/to JSON
 * \version 2017-03-09/AW: Compile error on fpc 3.0.0 fixed
 * \version 2017-03-22/AW: Point attribute scale factor added (#2552)
 * \version 2017-03-28/AW: Documentation of JSON load/save functions updated
 * \version 2017-11-09/AW: New function to suggest buffer data type (#2587)
 * \version 2018-03-09/AW: New attribute property "invalid value" added (#3047)
 * \version 2018-06-22/AW: Attribute length type changed to uint32 (#3117)
 * \version 2019-01-21/AW: New attribute property "lod settings" added
 * \version 2019-02-15/AW: Fix Pascal API wrapper of PointAttribute class
 * \version 2022-01-26/AW: Add optional point attribute tags (#4128)
 * \version 2022-03-18/AW: Add optional list of named values (#4128)
 * \version 2023-10-04/AW: Add attribute definition string size limits (#4790)
 *
 *******************************************************************************
 *}

unit riegl.rdb.pointcloud.pointAttribute;

{******************************************************************************}
{***} INTERFACE {**************************************************************}
{******************************************************************************}

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

uses
  Classes,
  SysUtils,
  riegl.rdb,
  riegl.rdb.context,
  riegl.rdb.pointcloud.dataTypes;

//---< RDB LIBRARY IMPORTS >----------------------------------------------------

//______________________________________________________________________________
{*!
 * \brief Default constructor
 *
 * All values are set to default values.
 *}
function rdb_pointcloud_point_attribute_new(
  Context       : TRDBContextHandle;                 //!< [in] library context
  var Attribute : TRDBPointcloudPointAttributeHandle //!< [out] handle of created object
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Destroy point attribute object
 *}
function rdb_pointcloud_point_attribute_delete(
  Context       : TRDBContextHandle;                 //!< [in] library context
  var Attribute : TRDBPointcloudPointAttributeHandle //!< [in] handle of object to delete
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
(*!
 * \brief Load settings from JSON string
 *
 * This function parses the given JSON string and applies all available
 * properties - missing properties are silently ignored (i.e. the value
 * remains unchanged). When parsing the JSON string fails, an exception
 * is thrown.
 *
 * Example JSON string:
 *
 *     {
 *         "name": "riegl.reflectance",
 *         "title": "Reflectance",
 *         "tags": "",
 *         "description": "Target surface reflectance",
 *         "unit_symbol": "dB",
 *         "length": 1,
 *         "resolution": 0.01,
 *         "minimum_value": -327.68,
 *         "maximum_value": 327.67,
 *         "default_value": 0.0,
 *         "invalid_value": null,
 *         "named_values": {},
 *         "storage_class": "variable",
 *         "compression_options": "shuffle",
 *         "lod_settings": "default",
 *         "scale_factor": 1.0
 *     }
 *)
function rdb_pointcloud_point_attribute_json_load(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  JSON      : TRDBString                          //!< [in] JSON string
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Save settings to JSON string
 * \see rdb_pointcloud_point_attribute_json_load()
 *}
function rdb_pointcloud_point_attribute_json_save(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var JSON  : TRDBString                          //!< [out] JSON string
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get buffer data type
 *
 * This function suggests a data type that is suitable to
 * construct a buffer for storing values of this attribute.
 *
 * The suggestion is based on the resolution, minimumValue
 * and maximumValue properties, all others are ignored.
 *}
function rdb_pointcloud_point_attribute_data_type(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBUInt32                          //!< [out] data type enum
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get unique attribute name (for queries)
 *}
function rdb_pointcloud_point_attribute_get_name(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set unique attribute name (for queries), string size limits: [1, 100]
 *}
function rdb_pointcloud_point_attribute_set_name(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get attribute title (for display)
 *}
function rdb_pointcloud_point_attribute_get_title(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set attribute title (for display), string size limits: [1, 100]
 *}
function rdb_pointcloud_point_attribute_set_title(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get attribute tags (comma separated, e.g. "position, transform")
 *}
function rdb_pointcloud_point_attribute_get_tags(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set attribute tags (comma separated, e.g. "position, transform"), string size limits: [0, 5000]
 *}
function rdb_pointcloud_point_attribute_set_tags(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get attribute description (for display)
 *}
function rdb_pointcloud_point_attribute_get_description(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set attribute description (for display), string size limits: [0, 5000]
 *}
function rdb_pointcloud_point_attribute_set_description(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get physical unit symbol (e.g. "m", "rad", "K")
 *}
function rdb_pointcloud_point_attribute_get_unit_symbol(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{!
 * \brief Set physical unit symbol (e.g. "m", "rad", "K"), string size limits: [0, 100]
 *}
function rdb_pointcloud_point_attribute_set_unit_symbol(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get number of dimensions/elements (1: scalar, >1: vector, e.g. 3 for point coordinates)
 *}
function rdb_pointcloud_point_attribute_get_length(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBUInt8                           //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get number of dimensions/elements (1: scalar, >1: vector, e.g. 3 for point coordinates)
 *}
function rdb_pointcloud_point_attribute_get_length_u32(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBUInt32                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set number of dimensions/elements (1: scalar, >1: vector, e.g. 3 for point coordinates)
 *}
function rdb_pointcloud_point_attribute_set_length(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBUInt8                           //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set number of dimensions/elements (1: scalar, >1: vector, e.g. 3 for point coordinates)
 *}
function rdb_pointcloud_point_attribute_set_length_u32(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBUInt32                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get expected value resolution
 *}
function rdb_pointcloud_point_attribute_get_resolution(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set expected value resolution
 *}
function rdb_pointcloud_point_attribute_set_resolution(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get theoretical minimum value
 *}
function rdb_pointcloud_point_attribute_get_minimum_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set theoretical minimum value
 *}
function rdb_pointcloud_point_attribute_set_minimum_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get theoretical maximum value
 *}
function rdb_pointcloud_point_attribute_get_maximum_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set theoretical maximum value
 *}
function rdb_pointcloud_point_attribute_set_maximum_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get default value
 *}
function rdb_pointcloud_point_attribute_get_default_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set default value
 *}
function rdb_pointcloud_point_attribute_set_default_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get invalid value
 *}
function rdb_pointcloud_point_attribute_get_invalid_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set invalid value
 *}
function rdb_pointcloud_point_attribute_set_invalid_value(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get list of VALUE=NAME pairs separated by a single line feed character (LF, ASCII 0x0A)
 *}
function rdb_pointcloud_point_attribute_get_named_values(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set list of VALUE=NAME pairs separated by a single line feed character (LF, ASCII 0x0A), string size limits: [0, 1000000]
 *}
function rdb_pointcloud_point_attribute_set_named_values(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get storage class
 *}
function rdb_pointcloud_point_attribute_get_storage_class(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Byte                         //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set storage class
 *}
function rdb_pointcloud_point_attribute_set_storage_class(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Byte                         //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get compression options
 *}
function rdb_pointcloud_point_attribute_get_compression_options(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Byte                         //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set compression options
 *}
function rdb_pointcloud_point_attribute_set_compression_options(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Byte                         //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get LOD settings
 *}
function rdb_pointcloud_point_attribute_get_lod_settings(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : TRDBString                          //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set LOD settings, string size limits: [0, 50000]
 *}
function rdb_pointcloud_point_attribute_set_lod_settings(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : TRDBString                          //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get scale factor
 *}
function rdb_pointcloud_point_attribute_get_scale_factor(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  var Value : System.Double                       //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set scale factor
 *}
function rdb_pointcloud_point_attribute_set_scale_factor(
  Context   : TRDBContextHandle;                  //!< [in] library context
  Attribute : TRDBPointcloudPointAttributeHandle; //!< [in] attribute handle
  Value     : System.Double                       //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//__________________________________________________________________________
{*!
 * \brief Get attribute name and vector index from attribute descriptor
 *
 * This function decodes the given attribute descriptor into attribute name and attribute
 * vector index. The optional vector index is enclosed in square brackets and follows the
 * point attribute name. If no vector index is given, then index -1 is returned. If the
 * vector index cannot be interpreted as a single positive integer value, then an empty
 * name and index -2 is returned.
 *
 * Examples:
 *   descriptor | name  | index
 *  ------------|-------|-------
 *    "xyz"     | "xyz" |  -1
 *    "xyz[0]"  | "xyz" |   0
 *    "rgb[1]"  | "rgb" |   1
 *
 * \note 'name' is expected to point to a string that is at least as long as
 *       the 'descriptor' string plus 10 extra characters.
 *
 * \returns the number of characters written to 'name'
 *}
function rdb_pointcloud_point_attribute_decode_descriptor(
  Descriptor : TRDBString;    //!< [in]  attribute descriptor, e.g. "xyz", "rgb[1]"
  Name       : TRDBString;    //!< [out] attribute name, e.g. "xyz", "rgb"
  var Index  : System.Integer //!< [out] attribute vector index, e.g. -1, 1
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//---< ENUM TRDBPointcloudStorageClass >----------------------------------------
{*!
 * \brief Point attribute storage class
 *}
type
  TRDBPointcloudStorageClass = (
    RDB_STORAGE_CONSTANT = 1, // value cannot be changed
    RDB_STORAGE_VARIABLE = 2, // value can change from time to time
    RDB_STORAGE_DYNAMIC  = 3  // value is likely to be changed often
  );

//---< ENUM TRDBPointcloudCompressionOptions >----------------------------------
{*!
 * \brief Point attribute compression options
 *}
type
  TRDBPointcloudCompressionOptions = (
    RDB_COMPRESSION_DEFAULT       = 0, // nothing special, just use default compression algorithm
    RDB_COMPRESSION_DELTA         = 1, // calculate differences between two consecutive values
    RDB_COMPRESSION_SHUFFLE       = 2, // shuffle bytes of point attribute values
    RDB_COMPRESSION_DELTA_SHUFFLE = 3  // calculate differences and shuffle bytes
  );

//---< CLASS TRDBPointcloudPointAttribute >-------------------------------------
{*!
 * \brief Point attribute description
 *
 * This class describes a point attribute. The database uses this
 * information for internal attribute representation and compression.
 *
 * While the name is a unique identifier, the description holds some
 * text that client programs might display the user in some way. Also
 * the physical unit is not used by the database but can be presented
 * to the user (see PointAttribute::unitSymbol).
 *
 * To avoid point attribute name conflicts, the names (not the titles)
 * shall contain a namespace prefix. Namespace and attribute name are
 * separated by a dot (".", e.g. "riegl.xyz"). The default namespace
 * "riegl" is used if no namespace is given.
 *
 * \remarks
 *
 * If the attribute is a vector (i.e. length > 1), then you might append
 * a zero-based vector element index to the attribute name when inserting,
 * updating or selecting points. Example: use "rgb[0]" to access the red
 * color component (0), the green (1) and blue (2) components are not read
 * or modified in this case.
 *
 * Furthermore, the minimum, maximum and default values are applied to all
 * elements of vectors and the vector length must be in the range [1..100000].
 *
 * PointAttribute::defaultValue is returned when reading a point attribute
 * that has never been written before. The value must be between minimum and
 * maximum (both inclusive).
 *
 * PointAttribute::invalidValue may be used to define a value that represents
 * an invalid/undefined/unknown value. The value must be between minimum and
 * maximum (both inclusive) and must be a multiple of the resolution value.
 * The value may be equal to the default value and you may use "NaN" (not a
 * number) to signal that there is no "invalid value".
 *
 * \note
 *
 * Attribute names may only contain following ASCII characters:
 *
 *   - a-z
 *   - A-Z
 *   - 0-9
 *   - .
 *   - _
 *
 * Attribute title, description and unit symbol may contain any UTF-8 character.
 *
 * \see riegl::rdb::pointcloud::PointAttributes
 *}
type
  TRDBPointcloudPointAttribute = class(System.TObject)
  public
    Name               : System.String;                    //!< unique attribute name (for queries) \see details of class PointAttribute, string size limits: [1, 100]
    Title              : System.String;                    //!< attribute title (for display), string size limits: [1, 100]
    Tags               : System.String;                    //!< attribute tags (comma separated, e.g. "position, transform"), string size limits: [0, 5000]
    Description        : System.String;                    //!< attribute description (for display), string size limits: [0, 5000]
    UnitSymbol         : System.String;                    //!< physical unit symbol (e.g. "m", "rad", "K"), string size limits: [0, 100]
    Length             : TRDBUInt32;                       //!< number of dimensions/elements (1: scalar, >1: vector, e.g. 3 for point coordinates)
    Resolution         : System.Double;                    //!< expected value resolution
    MinimumValue       : System.Double;                    //!< theoretical minimum value
    MaximumValue       : System.Double;                    //!< theoretical maximum value
    DefaultValue       : System.Double;                    //!< default value (minimum <= default <= maximum)
    InvalidValue       : System.Double;                    //!< invalid value (minimum <= invalid <= maximum, use "not-a-number" if there is no invalid value)
    NamedValues        : System.String;                    //!< list of VALUE=NAME pairs separated by a single line feed character (LF, ASCII 0x0A), string size limits: [0, 1000000]
    StorageClass       : TRDBPointcloudStorageClass;       //!< storage class
    CompressionOptions : TRDBPointcloudCompressionOptions; //!< compression options

    {*!
     * \brief Level of detail settings
     *
     * This field defines the method to be used to generate level of detail
     * data (LOD) for this point attribute. Depending on the LOD mode defined
     * during database creation (see CreateSettings::LodMode), several LOD
     * methods are available. A list of all methods and their settings can
     * be found in file "/manual/riegl_rdb_lod_methods.json" in the RDB SDK.
     *
     * String size limits: [0, 50000]
     *}
    LodSettings : System.String;

    {*!
     * \brief optional scale factor applied to real numbers (i.e. resolution not equal to 1.0)
     *
     *  - reading points: resultValue = originalValue * scaleFactor
     *  - writing points: storedValue = givenValue / scaleFactor
     *}
    ScaleFactor : System.Double;

  public
    destructor Destroy(); override;

    {*!
     * \brief Default constructor
     *
     * All properties are set to default values.
     *}
    constructor Create(const Context : TRDBContext); reintroduce; overload;

    {*!
     * \brief Copy constructor
     *
     * All properties are copied from the given settings object.
     *}
    constructor Create(const Settings : TRDBPointcloudPointAttribute); overload;

    {*!
     * \brief Assignment operator
     *
     * All properties are copied from the given settings object.
     *}
    function Assign(const Settings : TRDBPointcloudPointAttribute) : TRDBPointcloudPointAttribute;

    //__________________________________________________________________________
    {*!
     * \brief Get attribute name and vector index from attribute descriptor
     *
     * This function decodes the given attribute descriptor into attribute name and attribute
     * vector index. The optional vector index is enclosed in square brackets and follows the
     * point attribute name. If no vector index is given, then index -1 is returned. If the
     * vector index cannot be interpreted as a single positive integer value, then an empty
     * name and index -2 is returned.
     *
     * Examples:
     *   descriptor | name  | index
     *  ------------|-------|-------
     *    "xyz"     | "xyz" |  -1
     *    "xyz[0]"  | "xyz" |   0
     *    "rgb[1]"  | "rgb" |   1
     *}
    class procedure DecodeDescriptor(
        const Descriptor : System.String; //!< [in]  attribute descriptor, e.g. "xyz", "rgb[1]"
        var   Name       : System.String; //!< [out] attribute name, e.g. "xyz", "rgb"
        var   Index      : System.Integer //!< [out] attribute vector index, e.g. -1, 1
    );

    //__________________________________________________________________________
    {*!
     * \brief Get map from named values string
     *
     * This function decodes the `NamedValues` string into a map.
     *
     * \see NamedValues
     * \see SetNamedValues()
     *}
    function GetNamedValues : TStringList;

    //__________________________________________________________________________
    {*!
     * \brief Set named values string from map
     *
     * This function generates the `NamedValues` string from a map.
     *
     * \see NamedValues
     * \see GetNamedValues()
     *}
    procedure SetNamedValues(const Map : TStringList);

    (*!
     * \brief Load settings from JSON string
     *
     * This function parses the given JSON string and applies all available
     * properties - missing properties are silently ignored (i.e. the value
     * remains unchanged). When parsing the JSON string fails, an exception
     * is thrown.
     *
     * Example JSON string:
     *
     *     {
     *         "name": "riegl.reflectance",
     *         "title": "Reflectance",
     *         "tags": "",
     *         "description": "Target surface reflectance",
     *         "unit_symbol": "dB",
     *         "length": 1,
     *         "resolution": 0.01,
     *         "minimum_value": -327.68,
     *         "maximum_value": 327.67,
     *         "default_value": 0.0,
     *         "invalid_value": null,
     *         "named_values": {},
     *         "storage_class": "variable",
     *         "compression_options": "shuffle",
     *         "lod_settings": "default",
     *         "scale_factor": 1.0
     *     }
     *)
    procedure Load(const JSON : String);

    {*!
     * \brief Save settings to JSON string
     * \see load()
     *}
    function Save() : String;

    {*!
     * \brief Get buffer data type
     *
     * This function suggests a data type that is suitable to
     * construct a buffer for storing values of this attribute.
     *
     * The suggestion is based on the resolution, minimumValue
     * and maximumValue properties, all others are ignored.
     *}
     function DataType() : TRDBPointcloudDataType;

  private
    FContext : TRDBContext;
    FHandle  : TRDBPointcloudPointAttributeHandle;

  public
    property Handle : TRDBPointcloudPointAttributeHandle read FHandle;
  end;

//---< TOOLS >------------------------------------------------------------------

//______________________________________________________________________________
{*!
 * \brief Read attribute from library
 * \note For wrapper internal use only.
 *}
procedure PointcloudPointAttributeRead(const Target : TRDBPointcloudPointAttribute);

//______________________________________________________________________________
{*!
 * \brief Post attribute to library
 * \note For wrapper internal use only.
 *}
procedure PointcloudPointAttributePost(const Source : TRDBPointcloudPointAttribute);

{******************************************************************************}
{***} IMPLEMENTATION {*********************************************************}
{******************************************************************************}

//---< TRDBPointcloudPointAttribute::PUBLIC >-----------------------------------

destructor TRDBPointcloudPointAttribute.Destroy();
begin
  if (FContext <> nil) and (FHandle <> nil) then
  begin
    rdb_pointcloud_point_attribute_delete(FContext.Handle, FHandle);
  end;
  inherited;
end;

constructor TRDBPointcloudPointAttribute.Create(const Context : TRDBContext);
begin
  inherited Create;
  FContext := Context; FHandle := nil;
  FContext.Check(rdb_pointcloud_point_attribute_new(FContext.Handle, FHandle));
  PointcloudPointAttributeRead(Self);
end;

constructor TRDBPointcloudPointAttribute.Create(const Settings : TRDBPointcloudPointAttribute);
begin
  Create(Settings.FContext);
  Self.Assign(Settings);
end;

function TRDBPointcloudPointAttribute.Assign(const Settings : TRDBPointcloudPointAttribute) : TRDBPointcloudPointAttribute;
begin
  Self.Load(Settings.Save());
  Result := Self;
end;

class procedure TRDBPointcloudPointAttribute.DecodeDescriptor(
  const Descriptor : System.String;
  var   Name       : System.String;
  var   Index      : System.Integer
);
var
  Result : AnsiString;
begin
  SetLength(Result, System.Length(Descriptor) + 10);
  FillChar(Result[1], System.Length(Result), 0);
  rdb_pointcloud_point_attribute_decode_descriptor(
    TRDBString(AnsiToUtf8(Descriptor)), TRDBString(Result), Index
  );
  Name := AsSTDString(TRDBString(Result));
end;

function TRDBPointcloudPointAttribute.GetNamedValues : TStringList;
var
  Ptr, Eos  : PChar;
  Key, Name : String;
begin
  Result := TStringList.Create;
  try
    Result.Sorted := TRUE;
    Ptr := System.PChar(NamedValues);
    Eos := Ptr + System.Length(NamedValues);
    while (Ptr < Eos) do
    begin
      Key := '';
      while (Ptr < Eos) and (Ptr^ <> '=') do
      begin
        Key := Key + Ptr^;
        Inc(Ptr);
      end;
      if (Ptr < Eos) then Inc(Ptr) else Break;

      Name := '';
      while (Ptr < Eos) and (Ptr^ <> #$0A) do
      begin
        Name := Name + Ptr^;
        Inc(Ptr);
      end;
      if (Ptr < Eos) then Inc(Ptr);

      Result.Add(Trim(Key) + '=' + Trim(Name));
    end;
  except
    FreeAndNil(Result);
    raise;
  end;
end;

procedure TRDBPointcloudPointAttribute.SetNamedValues(const Map : TStringList);
var
  i : Integer;
begin
  NamedValues := '';
  for i := 0 to Map.Count-1 do
  begin
    NamedValues := NamedValues + Map.Names[i] + '=' + Map.ValueFromIndex[i] + #$0A;
  end;
end;

procedure TRDBPointcloudPointAttribute.Load(const JSON : String);
begin
  FContext.Check(rdb_pointcloud_point_attribute_json_load(
    FContext.Handle, FHandle, TRDBString(AnsiToUtf8(JSON))
  ));
  PointcloudPointAttributeRead(Self);
end;

function TRDBPointcloudPointAttribute.Save() : String;
var
  JSON : TRDBString;
begin
  PointcloudPointAttributePost(Self);
  FContext.Check(rdb_pointcloud_point_attribute_json_save(
    FContext.Handle, FHandle, JSON
  ));
  Result := AsSTDString(JSON);
end;

function TRDBPointcloudPointAttribute.DataType() : TRDBPointcloudDataType;
var
  Value : TRDBUInt32;
begin
  PointcloudPointAttributePost(Self);
  FContext.Check(rdb_pointcloud_point_attribute_data_type(
    FContext.Handle, FHandle, Value
  ));
  Result := TRDBPointcloudDataType(Value);
end;

//---< TOOLS >------------------------------------------------------------------

procedure PointcloudPointAttributeRead(const Target : TRDBPointcloudPointAttribute);
var
  Local : record
    Name               : TRDBString;
    Title              : TRDBString;
    Tags               : TRDBString;
    Description        : TRDBString;
    UnitSymbol         : TRDBString;
    NamedValues        : TRDBString;
    StorageClass       : System.Byte;
    CompressionOptions : System.Byte;
    LodSettings        : TRDBString;
  end;
begin
  FillChar(Local, SizeOf(Local), #0);
  //
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_name(
      Target.FContext.Handle, Target.Handle, Local.Name
  ));                                                                           Target.Name := AsSTDString(Local.Name);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_title(
      Target.FContext.Handle, Target.Handle, Local.Title
  ));                                                                           Target.Title := AsSTDString(Local.Title);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_tags(
      Target.FContext.Handle, Target.Handle, Local.Tags
  ));                                                                           Target.Tags := AsSTDString(Local.Tags);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_description(
      Target.FContext.Handle, Target.Handle, Local.Description
  ));                                                                           Target.Description := AsSTDString(Local.Description);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_unit_symbol(
      Target.FContext.Handle, Target.Handle, Local.UnitSymbol
  ));                                                                           Target.UnitSymbol := AsSTDString(Local.UnitSymbol);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_length_u32(
      Target.FContext.Handle, Target.Handle, Target.Length
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_resolution(
      Target.FContext.Handle, Target.Handle, Target.Resolution
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_minimum_value(
      Target.FContext.Handle, Target.Handle, Target.MinimumValue
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_maximum_value(
      Target.FContext.Handle, Target.Handle, Target.MaximumValue
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_default_value(
      Target.FContext.Handle, Target.Handle, Target.DefaultValue
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_invalid_value(
      Target.FContext.Handle, Target.Handle, Target.InvalidValue
  ));
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_named_values(
      Target.FContext.Handle, Target.Handle, Local.NamedValues
  ));                                                                           Target.NamedValues := AsSTDString(Local.NamedValues);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_storage_class(
      Target.FContext.Handle, Target.Handle, Local.StorageClass
  ));                                                                           Target.StorageClass := TRDBPointcloudStorageClass(Local.StorageClass);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_compression_options(
      Target.FContext.Handle, Target.Handle, Local.CompressionOptions
  ));                                                                           Target.CompressionOptions := TRDBPointcloudCompressionOptions(Local.CompressionOptions);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_lod_settings(
      Target.FContext.Handle, Target.Handle, Local.LodSettings
  ));                                                                           Target.LodSettings := AsSTDString(Local.LodSettings);
  Target.FContext.Check(rdb_pointcloud_point_attribute_get_scale_factor(
      Target.FContext.Handle, Target.Handle, Target.ScaleFactor
  ));
end;

procedure PointcloudPointAttributePost(const Source : TRDBPointcloudPointAttribute);
begin
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_name(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.Name))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_title(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.Title))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_tags(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.Tags))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_description(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.Description))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_unit_symbol(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.UnitSymbol))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_length_u32(
      Source.FContext.Handle, Source.Handle, Source.Length
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_resolution(
      Source.FContext.Handle, Source.Handle, Source.Resolution
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_minimum_value(
      Source.FContext.Handle, Source.Handle, Source.MinimumValue
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_maximum_value(
      Source.FContext.Handle, Source.Handle, Source.MaximumValue
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_default_value(
      Source.FContext.Handle, Source.Handle, Source.DefaultValue
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_invalid_value(
      Source.FContext.Handle, Source.Handle, Source.InvalidValue
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_named_values(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.NamedValues))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_storage_class(
      Source.FContext.Handle, Source.Handle, Ord(Source.StorageClass)
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_compression_options(
      Source.FContext.Handle, Source.Handle, Ord(Source.CompressionOptions)
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_lod_settings(
      Source.FContext.Handle, Source.Handle, TRDBString(AnsiToUtf8(Source.LodSettings))
  ));
  Source.FContext.Check(rdb_pointcloud_point_attribute_set_scale_factor(
      Source.FContext.Handle, Source.Handle, Source.ScaleFactor
  ));
end;

end.
