{*
 *******************************************************************************
 *
 *  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    riegl.rdb.pointcloud.pas
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Main point cloud database class (Pascal wrapper code)
 * \version 2015-10-14/AW: Initial version
 * \version 2016-11-04/AW: Allow to read from multiple nodes at once (#2368)
 * \version 2016-11-08/AW: New "fill query" (class QueryFill) added (#1926)
 * \version 2016-11-16/AW: New function to output database statistics added
 * \version 2016-11-21/AW: New "invert query" (class QueryInvert) added (#2406)
 * \version 2017-03-21/AW: Database management interface added (#2550)
 * \version 2017-08-22/AW: Added function to query database UUID (#2720)
 * \version 2018-05-28/AW: New CreateDatabase() that accepts a schema (#3109)
 * \version 2019-02-15/AW: Fix C++ API wrapper of (Create|Open)Settings classes
 * \version 2020-02-24/AW: New function to check if a database is empty (#3566)
 * \version 2020-07-03/AW: Changelog management interface added (#3614)
 *
 *******************************************************************************
 *}

unit riegl.rdb.pointcloud;

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

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

uses
  riegl.rdb,
  riegl.rdb.context,
  riegl.rdb.pointcloud.createSettings,
  riegl.rdb.pointcloud.openSettings,
  riegl.rdb.pointcloud.management,
  riegl.rdb.pointcloud.changelog,
  riegl.rdb.pointcloud.metaData,
  riegl.rdb.pointcloud.pointAttributes,
  riegl.rdb.pointcloud.transactions,
  riegl.rdb.pointcloud.graphNode,
  riegl.rdb.pointcloud.queryInsert,
  riegl.rdb.pointcloud.queryUpdate,
  riegl.rdb.pointcloud.queryRemove,
  riegl.rdb.pointcloud.querySelect,
  riegl.rdb.pointcloud.queryStat,
  riegl.rdb.pointcloud.queryFill,
  riegl.rdb.pointcloud.queryInvert;

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

//______________________________________________________________________________
{*!
 * \brief Create Pointcloud instance
 *
 * This creates a new Pointcloud object instance. To actually access or
 * create a point cloud database file, you must call open() or create().
 *
 * Each Pointcloud instance may only be accessed from one thread/process at
 * a time. Opening the same database using different Pointcloud instances
 * (in the same or different thread or process) is allowed.
 *
 * \note The implicit copy-constructor and assignment-operators will yield
 *       to multiple instances pointing to the same database file. This
 *       is okay, as long as you do not intend to use both instances in
 *       different threads simultaneously.
 *
 * \see rdb_pointcloud_create()
 * \see rdb_pointcloud_open()
 *}
function rdb_pointcloud_new(
  Context        : TRDBContextHandle;   //!< [in] library context
  var Pointcloud : TRDBPointcloudHandle //!< [out] handle of created object
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Destroy Pointcloud instance
 *
 * \note If there are multiple Pointcloud instances pointing to the same
 *       file (see constructor notes), then the database file will be
 *       closed when the last Pointcloud instance is deleted.
 *}
function rdb_pointcloud_delete(
  Context        : TRDBContextHandle;   //!< [in] library context
  var Pointcloud : TRDBPointcloudHandle //!< [in] handle of object to delete
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Create new database
 *
 * This function creates a new (empty) database. If the given
 * database already exists, it will be overwritten (unless it is
 * opened by other clients, in which case the creation will fail).
 * The target folder must exist - it is not created automatically.
 * If the database could not be created, an exception is thrown.
 *}
function rdb_pointcloud_create(
  Context    : TRDBContextHandle;                 //!< [in] library context
  Pointcloud : TRDBPointcloudHandle;              //!< [in] point cloud
  Location   : TRDBString;                        //!< [in] database location (filename)
  Settings   : TRDBPointcloudCreateSettingsHandle //!< [in] database creation settings
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Create new database
 *
 * \copydoc rdb_pointcloud_create()
 *
 * Additionally, all required point attributes and metadata entries as defined
 * by the schema are added to the database. The schema is given in JSON format,
 * details see rdb_pointcloud_management_validate(). If 'optionals' is not '0',
 * then also the optional point attributes and metadata entries are added to
 * the database.
 *
 * \note
 *   Only RIEGL default point attributes and metadata entries are supported.
 *   Metadata entries are added but have no value (empty string).
 *
 * \note
 *   If the schema defines a primary point attribute, then it overrides
 *   the primary point attribute defined in the 'settings' parameter.
 *
 * \see rdb_pointcloud_management_validate()
 *}
function rdb_pointcloud_create_with_schema(
  Context    : TRDBContextHandle;                  //!< [in] library context
  Pointcloud : TRDBPointcloudHandle;               //!< [in] point cloud
  Location   : TRDBString;                         //!< [in] database location (filename)
  Settings   : TRDBPointcloudCreateSettingsHandle; //!< [in] database creation settings
  Schema     : TRDBString;                         //!< [in] database schema (JSON format)
  Optionals  : TRDBUInt32                          //!< [in] >0: include optional items
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Open existing database
 *
 * Open existing database location.
 * If the given database does not exist, an exception is thrown.
 *}
function rdb_pointcloud_open(
  Context    : TRDBContextHandle;               //!< [in] library context
  Pointcloud : TRDBPointcloudHandle;            //!< [in] point cloud
  Location   : TRDBString;                      //!< [in] database location (filename)
  Settings   : TRDBPointcloudOpenSettingsHandle //!< [in] database open settings
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Close database
 *
 * Close database file and release all internal resources.
 * This function fails if there are pending transactions.
 *}
function rdb_pointcloud_close(
  Context    : TRDBContextHandle;   //!< [in] library context
  Pointcloud : TRDBPointcloudHandle //!< [in] point cloud
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Check if a database is open
 *}
function rdb_pointcloud_is_open(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud
  var isOpen : Cardinal              //!< [out] 0: not open, 1: open
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Check if a database is empty
 *}
function rdb_pointcloud_is_empty(
  Context     : TRDBContextHandle;    //!< [in] library context
  Pointcloud  : TRDBPointcloudHandle; //!< [in] point cloud
  var isEmpty : Cardinal              //!< [out] 0: not empty, 1: empty or not open
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get database file's UUID
 * \returns the Universally Unique Identifier of the database file
 *
 * \note Database files created with rdblib prior version 2.1.2 do
 *       not have a UUID. In this case the "nil" UUID is returned.
 *       If no database is opened, an empty string is returned.
 *}
function rdb_pointcloud_get_uuid(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud
  var Output : TRDBString            //!< [out] result string
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief File statistics and debugging information
 *
 * This function returns statistics and debugging information about
 * the database file which is intended for factory usage only (i.e.
 * the format may change at any time and the content is undocumented).
 *}
function rdb_pointcloud_inspect(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud
  Format     : TRDBUInt8;            //!< [in] output format
  var Output : TRDBString            //!< [out] result string
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Clear internal data cache
 *
 * This function clears (flushes) the internal data cache and reduces
 * memory consumption as much as possible.
 *}
function rdb_pointcloud_clear_cache(
  Context    : TRDBContextHandle;   //!< [in] library context
  Pointcloud : TRDBPointcloudHandle //!< [in] point cloud
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//---< CLASS TRDBPointcloud >---------------------------------------------------
{*!
 * \brief Main point cloud database class
 *
 * Use this class to create or open a point cloud database and insert, update
 * or query points.
 *
 * \note All functions of this class throw an Exception of class
 *       riegl::rdb::Error in case of troubles.
 *}
type
  TRDBPointcloud = class(System.TObject)
  //____________________________________________________________________________
  //
  //! \name Constructors and destructor
  //! \{
  public
    {*!
     * \brief Create Pointcloud instance
     *
     * This creates a new Pointcloud object instance. To actually access or
     * create a point cloud database file, you must call open() or create().
     *
     * Each Pointcloud instance may only be accessed from one thread/process at
     * a time. Opening the same database using different Pointcloud instances
     * (in the same or different thread or process) is allowed.
     *
     * \note The implicit copy-constructor and assignment-operators will yield
     *       to multiple instances pointing to the same database file. This
     *       is okay, as long as you do not intend to use both instances in
     *       different threads simultaneously.
     *
     * \see Pointcloud::create()
     * \see Pointcloud::open()
     *}
    constructor Create(Context : TRDBContext); reintroduce;

    {*!
     * \brief Destroy Pointcloud instance
     *
     * \note If there are multiple Pointcloud instances pointing to the same
     *       file (see constructor notes), then the database file will be
     *       closed when the last Pointcloud instance is deleted.
     *}
    destructor Destroy(); override;
  //! \}
  //____________________________________________________________________________
  //
  //! \name Database Access
  //! \{
  public
    {*!
     * \brief Create new database
     *
     * This function creates a new (empty) database. If the given
     * database already exists, it will be overwritten (unless it is
     * opened by other clients, in which case the creation will fail).
     * The target folder must exist - it is not created automatically.
     * If the database could not be created, an exception is thrown.
     *}
    procedure CreateDatabase(
      const Location : System.String;               //!< [in] database location (filename)
      const Settings : TRDBPointcloudCreateSettings //!< [in] database creation settings
    ); overload;

    {*!
     * \brief Create new database
     *
     * \copydoc CreateDatabase()
     *
     * Additionally, all required point attributes and metadata entries as defined
     * by the schema are added to the database. The schema is given in JSON format,
     * details see riegl::rdb::pointcloud::Management::validate(). If 'optionals'
     * is 'true', then also the optional point attributes and metadata entries
     * are added to the database.
     *
     * \note
     *   Only RIEGL default point attributes and metadata entries are supported.
     *   Metadata entries are added but have no value (empty string).
     *
     * \note
     *   If the schema defines a primary point attribute, then it overrides
     *   the primary point attribute defined in the 'settings' parameter.
     *
     * \see riegl::rdb::pointcloud::Management::validate()
     *}
    procedure CreateDatabase(
      const Location  : System.String;                //!< [in] database location (filename)
      const Settings  : TRDBPointcloudCreateSettings; //!< [in] database creation settings
      const Schema    : System.String;                //!< [in] database schema (JSON format)
      const Optionals : System.Boolean=FALSE          //!< [in] >0: include optional items
    ); overload;

    {*!
     * \brief Open existing database
     *
     * Open existing database location.
     * If the given database does not exist, an exception is thrown.
     *}
    procedure OpenDatabase(
      const Location : System.String;             //!< [in] database location (filename)
      const Settings : TRDBPointcloudOpenSettings //!< [in] database open settings
    );

    {*!
     * \brief Close database
     *
     * Close database file and release all internal resources.
     * This function fails if there are pending transactions.
     *}
    procedure CloseDatabase();

    {*!
     * \brief Check if a database is open
     * \returns true if a database is open
     *}
    function IsOpen() : Boolean;

    {*!
     * \brief Check if a database is empty
     * \returns true if the database contains no points or no database is open
     *}
    function IsEmpty() : Boolean;

    {*!
     * \brief Get database file's UUID
     * \returns the Universally Unique Identifier of the database file
     *
     * \note Database files created with rdblib prior version 2.1.2 do
     *       not have a UUID. In this case the "nil" UUID is returned.
     *       If no database is opened, an empty string is returned.
     *}
    function GetUUID() : System.String;

    {*!
     * \brief File statistics and debugging information
     *
     * This function returns statistics and debugging information about
     * the database file which is intended for factory usage only (i.e.
     * the format may change at any time and the content is undocumented).
     *}
    function Inspect(const Format : TRDBUInt8) : System.String;

    {*!
     * \brief Clear internal data cache
     *
     * This function clears (flushes) the internal data cache and reduces
     * memory consumption as much as possible.
     *}
    procedure ClearCache();
  //! \}
  //____________________________________________________________________________
  //
  //! \name Database Management
  //! \{
  public
    {*!
     * \brief Basic point cloud management interface
     * \see riegl::rdb::pointcloud::Management
     * \note Do =NOT= delete the returned object!
     *}
    function Management() : TRDBPointcloudManagement;

    {*!
     * \brief Manage point cloud changelog
     * \see riegl::rdb::pointcloud::Changelog
     * \note Do =NOT= delete the returned object!
     *}
    function Changelog() : TRDBPointcloudChangelog;

    {*!
     * \brief Manage point cloud meta data
     * \see riegl::rdb::pointcloud::MetaData
     * \note Do =NOT= delete the returned object!
     *}
    function MetaData() : TRDBPointcloudMetaData;

    {*!
     * \brief Manage point attributes
     * \see riegl::rdb::pointcloud::PointAttributes
     * \note Do =NOT= delete the returned object!
     *}
    function PointAttribute() : TRDBPointcloudPointAttributes;

    {*!
     * \brief Manage point cloud transactions
     * \see riegl::rdb::pointcloud::Transactions
     * \note Do =NOT= delete the returned object!
     *}
    function Transaction() : TRDBPointcloudTransactions;
  //! \}
  //____________________________________________________________________________
  //
  //! \name Point Queries
  //! \{
  public
    {*!
     * \brief Insert points
     *
     * This function creates a new query object that can be used to
     * insert (new) points into the database.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Insert() : TRDBPointcloudQueryInsert;

    {*!
     * \brief Update points
     *
     * This function creates a new query object that can be used to
     * update (modify) attributes of existing points.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Update() : TRDBPointcloudQueryUpdate;

    {*!
     * \brief Select points
     *
     * This function creates a new query object that can be used to
     * select (read) attributes of existing points.<br>
     * The optional filter expression can be used to select particular
     * points - if no filter is given, all points will be loaded.<br>
     *
     * __Filter expression operators:__
     *
     *   | operator | meaning                                  |
     *   | :------: | ---------------------------------------- |
     *   |    ==    | equality (left == right)                 |
     *   |    !=    | inequality (left != right)               |
     *   |    <     | less than (left < right)                 |
     *   |    <=    | less than or equal to (left <= right)    |
     *   |    >     | greater than (left > right)              |
     *   |    >=    | greater than or equal to (left >= right) |
     *   |    &&    | conjunction (left && right)              |
     *   |    \|\|  | disjunction (left \|\| right)            |
     *   |    %     | unsigned modulo (left % right)           |
     *
     * __Filter expression syntax:__
     *
     *   - _constant_ = _integer_ | _double_ | _variable_ > ':' > ('minimum' | 'maximum' | 'default' | 'invalid')
     *   - _variable_ = [a-zA-Z] > ([0-9a-zA-Z] | '_' | '.')*
     *   - _operand_ = _constant_ | _variable_ | _expression_
     *   - _expression_ = (_operand_)? > _operator_ > _operand_
     *
     * __Filter expression examples:__
     *
     *   - "" (empty string means "all points")
     *   - "amplitude > 5.0"
     *   - "(reflectance > 0.0) && (selected == 1)"
     *   - "point_count != point_count:invalid"
     *   - "(id % 2) == 0"
     *
     * __Stored filters:__
     *
     *   Filters can also be stored in the metadata as `riegl.stored_filters`.
     *   All activated filters of this list will be applied in addition to the
     *   filter specified by the application (conjunction). To temporarily ignore
     *   (override) all stored filters, insert `"!!"` at the beginning or end of
     *   the filter string (e.g. `"!! riegl.id == 1"` or `"riegl.id == 1 !!"`).
     *
     * \note The resolution of a point attribute is used as a tolerance when comparing
     *       attribute values with constants (i.e. constants are rounded to the nearest
     *       multiple of the attribute resolution before comparison).
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Select(
      const Filter : String = '' //!< [in] optional point filter expression
    ) : TRDBPointcloudQuerySelect; overload;

    {*!
     * \brief Select points by index node
     *
     * \copydetails select()
     *
     * Instead of loading all points of the point cloud this variant just loads
     * the points contained in a single node. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Select(
      const Node   : TRDBPointcloudGraphNodeID; //!< [in] ID of index graph node
      const Filter : String = ''                //!< [in] optional point filter expression
    ) : TRDBPointcloudQuerySelect; overload;

    {*!
     * \brief Select points by index nodes
     *
     * \copydetails select()
     *
     * Instead of loading all points of the point cloud this variant just loads
     * the points contained in the given nodes. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Select(
      const Nodes  : TRDBPointcloudGraphNodeIDArray; //!< [in] IDs of index graph nodes
      const Filter : String = ''                     //!< [in] optional point filter expression
    ) : TRDBPointcloudQuerySelect; overload;

    {*!
     * \brief Fill points
     *
     * This function creates a new query object that can be used to
     * set (modify) attributes of existing points.
     *
     * Please have a look at select() for details about the filter expression.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Fill(
      const Filter : String = '' //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryFill; overload;

    {*!
     * \brief Fill points by index node
     *
     * \copydetails fill()
     *
     * Instead of modifying all points of the point cloud this variant just modifies
     * the points contained in a single node. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Fill(
      const Node   : TRDBPointcloudGraphNodeID; //!< [in] ID of index graph node
      const Filter : String = ''                //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryFill; overload;

    {*!
     * \brief Fill points by index nodes
     *
     * \copydetails fill()
     *
     * Instead of modifying all points of the point cloud this variant just modifies
     * the points contained in the given nodes. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Fill(
      const Nodes  : TRDBPointcloudGraphNodeIDArray; //!< [in] IDs of index graph nodes
      const Filter : String = ''                     //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryFill; overload;

    {*!
     * \brief Invert points
     *
     * This function creates a new query object that can be used to
     * invert attributes of existing points.
     *
     * Please have a look at select() for details about the filter expression.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Invert(
      const Filter : String = '' //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryInvert; overload;

    {*!
     * \brief Invert points by index node
     *
     * \copydetails invert()
     *
     * Instead of modifying all points of the point cloud this variant just modifies
     * the points contained in a single node. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Invert(
      const Node   : TRDBPointcloudGraphNodeID; //!< [in] ID of index graph node
      const Filter : String = ''                //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryInvert; overload;

    {*!
     * \brief Invert points by index nodes
     *
     * \copydetails invert()
     *
     * Instead of modifying all points of the point cloud this variant just modifies
     * the points contained in the given nodes. See notes about LOD for details.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Invert(
      const Nodes  : TRDBPointcloudGraphNodeIDArray; //!< [in] IDs of index graph nodes
      const Filter : String = ''                     //!< [in] optional point filter expression
    ) : TRDBPointcloudQueryInvert; overload;

    {*!
     * \brief Remove points
     *
     * This function creates a new query object that can be used to
     * remove (delete) existing points.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Remove() : TRDBPointcloudQueryRemove;

    {*!
     * \brief Query point statistics
     *
     * This function creates a new query object that can be used to
     * get point attribute statistics like minimum and maximum value.
     *
     * \note Bear in mind to delete the returned object when done!
     *}
    function Stat() : TRDBPointcloudQueryStat;
  //! \}
  //____________________________________________________________________________
  //
  public
    Context    : TRDBContext;
    Pointcloud : TRDBPointcloudHandle;
  private
    FManagement      : TRDBPointcloudManagement;
    FChangelog       : TRDBPointcloudChangelog;
    FMetaData        : TRDBPointcloudMetaData;
    FPointAttributes : TRDBPointcloudPointAttributes;
    FTransactions    : TRDBPointcloudTransactions;
  end;

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

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

uses
  Math,
  SysUtils;

//---< TRDBPointcloud::PUBLIC >-------------------------------------------------

constructor TRDBPointcloud.Create(Context : TRDBContext);
begin
  inherited Create;
  Self.Pointcloud := nil;
  Self.Context := Context;
  Context.Check(rdb_pointcloud_new(Context.Handle, Self.Pointcloud));
  Self.FManagement      := TRDBPointcloudManagement     .Create(Self);
  Self.FChangelog       := TRDBPointcloudChangelog      .Create(Self);
  Self.FMetaData        := TRDBPointcloudMetaData       .Create(Self);
  Self.FPointAttributes := TRDBPointcloudPointAttributes.Create(Self);
  Self.FTransactions    := TRDBPointcloudTransactions   .Create(Self);
end; // Create()

destructor TRDBPointcloud.Destroy();
begin
  FreeAndNil(FManagement);
  FreeAndNil(FChangelog);
  FreeAndNil(FMetaData);
  FreeAndNil(FPointAttributes);
  FreeAndNil(FTransactions);
  Context.Check(rdb_pointcloud_delete(Context.Handle, Self.Pointcloud));
  inherited;
end; // Destroy()

procedure TRDBPointcloud.CreateDatabase(
  const Location : System.String;
  const Settings : TRDBPointcloudCreateSettings
);
begin
  PointcloudCreateSettingsPost(Settings);
  Context.Check(rdb_pointcloud_create(
    Context.Handle, Pointcloud,
    TRDBString(AnsiToUtf8(Location)),
    Settings.Handle
  ));
end; // CreateDatabase()

procedure TRDBPointcloud.CreateDatabase(
  const Location  : System.String;
  const Settings  : TRDBPointcloudCreateSettings;
  const Schema    : System.String;
  const Optionals : System.Boolean
);
begin
  PointcloudCreateSettingsPost(Settings);
  Context.Check(rdb_pointcloud_create_with_schema(
    Context.Handle, Pointcloud,
    TRDBString(AnsiToUtf8(Location)), Settings.Handle,
    TRDBString(AnsiToUtf8(Schema)), IfThen(Optionals, 1, 0)
  ));
end; // CreateDatabase()

procedure TRDBPointcloud.OpenDatabase(
  const Location : System.String;
  const Settings : TRDBPointcloudOpenSettings
);
begin
  PointcloudOpenSettingsPost(Settings);
  Context.Check(rdb_pointcloud_open(
    Context.Handle, Pointcloud,
    TRDBString(AnsiToUtf8(Location)),
    Settings.Handle
  ));
end; // OpenDatabase()

procedure TRDBPointcloud.CloseDatabase();
begin
  Context.Check(rdb_pointcloud_close(Context.Handle, Pointcloud));
end; // CloseDatabase()

function TRDBPointcloud.IsOpen() : Boolean;
var
  Return : Cardinal;
begin
  Return := 0;
  Context.Check(rdb_pointcloud_is_open(Context.Handle, Pointcloud, Return));
  Result := (not (Return = 0));
end; // IsOpen()

function TRDBPointcloud.IsEmpty() : Boolean;
var
  Return : Cardinal;
begin
  Return := 0;
  Context.Check(rdb_pointcloud_is_empty(Context.Handle, Pointcloud, Return));
  Result := (not (Return = 0));
end; // IsEmpty()

function TRDBPointcloud.GetUUID() : System.String;
var
  Output : TRDBString;
begin
  Output := nil;
  Context.Check(rdb_pointcloud_get_uuid(Context.Handle, Pointcloud, Output));
  Result := AsSTDString(Output);
end; // Inspect()

function TRDBPointcloud.Inspect(const Format : TRDBUInt8) : System.String;
var
  Output : TRDBString;
begin
  Output := nil;
  Context.Check(rdb_pointcloud_inspect(Context.Handle, Pointcloud, Format, Output));
  Result := AsSTDString(Output);
end; // Inspect()

procedure TRDBPointcloud.ClearCache();
begin
  Context.Check(rdb_pointcloud_clear_cache(Context.Handle, Pointcloud));
end; // ClearCache()

function TRDBPointcloud.Management() : TRDBPointcloudManagement;
begin
  Result := FManagement;
end; // Management()

function TRDBPointcloud.Changelog() : TRDBPointcloudChangelog;
begin
  Result := FChangelog;
end; // Changelog()

function TRDBPointcloud.MetaData() : TRDBPointcloudMetaData;
begin
  Result := FMetaData;
end; // MetaData()

function TRDBPointcloud.PointAttribute() : TRDBPointcloudPointAttributes;
begin
  Result := FPointAttributes;
end; // PointAttribute()

function TRDBPointcloud.Transaction() : TRDBPointcloudTransactions;
begin
  Result := FTransactions;
end; // Transaction()

function TRDBPointcloud.Insert() : TRDBPointcloudQueryInsert;
begin
  Result := TRDBPointcloudQueryInsert.Create(Self);
end; // Insert()

function TRDBPointcloud.Update() : TRDBPointcloudQueryUpdate;
begin
  Result := TRDBPointcloudQueryUpdate.Create(Self);
end; // Update()

function TRDBPointcloud.Select(const Filter : String) : TRDBPointcloudQuerySelect;
begin
  Result := TRDBPointcloudQuerySelect.Create(Self, nil, Filter);
end; // Select()

function TRDBPointcloud.Select(
  const Node   : TRDBPointcloudGraphNodeID;
  const Filter : String
) : TRDBPointcloudQuerySelect;
var
  Nodes : TRDBPointcloudGraphNodeIDArray;
begin
  SetLength(Nodes, 1); Nodes[0] := Node;
  if (Node <> 0) then
       Result := TRDBPointcloudQuerySelect.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQuerySelect.Create(); // empty/invalid
end; // Select()

function TRDBPointcloud.Select(
  const Nodes  : TRDBPointcloudGraphNodeIDArray;
  const Filter : String
) : TRDBPointcloudQuerySelect;
begin
  if (Length(Nodes) > 0) then
       Result := TRDBPointcloudQuerySelect.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQuerySelect.Create(); // empty/invalid
end; // Select()

function TRDBPointcloud.Fill(const Filter : String) : TRDBPointcloudQueryFill;
begin
  Result := TRDBPointcloudQueryFill.Create(Self, nil, Filter);
end; // Fill()

function TRDBPointcloud.Fill(
  const Node   : TRDBPointcloudGraphNodeID;
  const Filter : String
) : TRDBPointcloudQueryFill;
var
  Nodes : TRDBPointcloudGraphNodeIDArray;
begin
  SetLength(Nodes, 1); Nodes[0] := Node;
  if (Node <> 0) then
       Result := TRDBPointcloudQueryFill.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQueryFill.Create(); // empty/invalid
end; // Fill()

function TRDBPointcloud.Fill(
  const Nodes  : TRDBPointcloudGraphNodeIDArray;
  const Filter : String
) : TRDBPointcloudQueryFill;
begin
  if (Length(Nodes) > 0) then
       Result := TRDBPointcloudQueryFill.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQueryFill.Create(); // empty/invalid
end; // Fill()

function TRDBPointcloud.Invert(const Filter : String) : TRDBPointcloudQueryInvert;
begin
  Result := TRDBPointcloudQueryInvert.Create(Self, nil, Filter);
end; // Invert()

function TRDBPointcloud.Invert(
  const Node   : TRDBPointcloudGraphNodeID;
  const Filter : String
) : TRDBPointcloudQueryInvert;
var
  Nodes : TRDBPointcloudGraphNodeIDArray;
begin
  SetLength(Nodes, 1); Nodes[0] := Node;
  if (Node <> 0) then
       Result := TRDBPointcloudQueryInvert.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQueryInvert.Create(); // empty/invalid
end; // Invert()

function TRDBPointcloud.Invert(
  const Nodes  : TRDBPointcloudGraphNodeIDArray;
  const Filter : String
) : TRDBPointcloudQueryInvert;
begin
  if (Length(Nodes) > 0) then
       Result := TRDBPointcloudQueryInvert.Create(Self, @Nodes, Filter)
  else Result := TRDBPointcloudQueryInvert.Create(); // empty/invalid
end; // Invert()

function TRDBPointcloud.Remove() : TRDBPointcloudQueryRemove;
begin
  Result := TRDBPointcloudQueryRemove.Create(Self);
end; // Remove()

function TRDBPointcloud.Stat() : TRDBPointcloudQueryStat;
begin
  Result := TRDBPointcloudQueryStat.Create(Self);
end; // Remove()

end.
