{*
 *******************************************************************************
 *
 *  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.management.pas
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Basic point cloud management interface (Pascal wrapper code)
 * \version 2017-03-21/AW: Initial version
 * \version 2017-04-13/AW: Functions finalize() and vacuum() added
 * \version 2018-05-25/AW: Function validate() added (#3109)
 * \version 2019-01-19/AW: Parameter 'lodMode' added
 *
 * \see riegl::rdb::Pointcloud::management()
 *
 *******************************************************************************
 *}

unit riegl.rdb.pointcloud.management;

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

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

uses
  riegl.rdb,
  riegl.rdb.context,
  riegl.rdb.progress,
  riegl.rdb.pointcloud.createSettings;

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

//______________________________________________________________________________
{*!
 * \brief Query level of detail mode
 * \see rdb::riegl::pointcloud::Management::LodMode
 *}
function rdb_pointcloud_management_get_lod_mode(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  var Value  : TRDBUInt32            //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Modify level of detail mode
 * \see rdb::riegl::pointcloud::Management::LodMode
 *}
function rdb_pointcloud_management_set_lod_mode(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Value      : TRDBUInt32            //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Query level of detail size
 * \see rdb::riegl::pointcloud::Management::LodMode
 *}
function rdb_pointcloud_management_get_chunk_size_lod(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  var Value  : TRDBUInt32            //!< [out] current value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Modify level of detail size
 * \see rdb::riegl::pointcloud::Management::LodMode
 *}
function rdb_pointcloud_management_set_chunk_size_lod(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Value      : TRDBUInt32            //!< [in] new value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Dismiss database history
 *
 * This function deletes all transactions except the first (database
 * creation) and the current transaction (last committed or restored).
 * Please note that this operation only removes the transactions from
 * the database history and releases the related data blocks in the
 * database file so that they can be re-used by subsequent transactions.
 * However the database file size will not decrease unless you call
 * vacuum().
 *}
function rdb_pointcloud_management_finalize(
  Context    : TRDBContextHandle;   //!< [in] library context
  Pointcloud : TRDBPointcloudHandle //!< [in] point cloud handle
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Optimize database file
 *
 * This function reorganizes the data blocks in the database file so
 * that there are no (or as few as possible) unused blocks (gaps).
 * This is especially helpful after deleting point attributes or
 * calling finalize().
 *
 * \note This might be a lengthy operation and no other client can
 *       access the database in the meantime, not even to read.
 *}
function rdb_pointcloud_management_vacuum(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Progress   : TRDBProgress;         //!< [in] vacuum progress callback function
  Userdata   : Pointer               //!< [in] progress callback function user data
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
(*!
 * \brief Validate database file
 *
 * This function checks whether the database corresponds to the given schema.
 * The schema contains a list of required and optional point attributes and
 * metadata entries and is given in JSON format. Primary point attributes
 * are marked with a "*", optional attributes or metadata entries are
 * marked with a "?" appended to the name, all other items are required.
 *
 * The database must at least contain all primary and required point attributes
 * and all required metadata entries to correspond to the schema. If "strict"
 * is not "0", then the database additionally is not allowed to contain extra
 * point attributes or metadata entries that are not listed in the schema.
 *
 * If the database does not correspond to the schema, the function
 * returns 'RDB_FAILURE' (0) and 'RDB_SUCCESS' (1) otherwise.
 *
 * Example schema JSON string:
 *
 *     {
 *         "extension": "rdbx",
 *         "attributes": [
 *             "riegl.xyz*",
 *             "riegl.timestamp",
 *             "riegl.class?"
 *         ],
 *         "metadata": [
 *             "riegl.geo_tag",
 *             "riegl.device?"
 *         ]
 *     }
 *)
function rdb_pointcloud_management_validate(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Schema     : TRDBString;           //!< [in] database schema (JSON)
  Strict     : TRDBUInt32            //!< [in] >0: strict mode
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//---< CLASS TRDBPointcloudManagement >-----------------------------------------
{*!
 * \brief Basic point cloud management interface
 *
 * \see riegl::rdb::Pointcloud::management()
 *}
type
  TRDBPointcloudManagement = class(System.TObject)
  public
    {*!
     * \brief Constructor
     * \note  You cannot create new TRDBPointcloudManagement objects directly,
     *        use riegl::rdb::Pointcloud::management() instead.
     *}
    constructor Create(Parent : System.TObject); reintroduce;
    destructor  Destroy; override;

    {*!
     * \brief Query level of detail mode
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     *}
    function GetLodMode() : TRDBPointcloudLodMode;

    {*!
     * \brief Modify level of detail mode
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     *}
    procedure SetLodMode(const Value : TRDBPointcloudLodMode);

    {*!
     * \brief Query level of detail size
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     *}
    function GetChunkSizeLOD() : TRDBUInt32;

    {*!
     * \brief Modify level of detail size
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     *}
    procedure SetChunkSizeLOD(const Value : TRDBUInt32);

    {*!
     * \brief Dismiss database history
     *
     * This function deletes all transactions except the first (database
     * creation) and the current transaction (last committed or restored).
     * Please note that this operation only removes the transactions from
     * the database history and releases the related data blocks in the
     * database file so that they can be re-used by subsequent transactions.
     * However the database file size will not decrease unless you call
     * vacuum().
     *}
    procedure Finalize();

    {*!
     * \brief Optimize database file
     *
     * This function reorganizes the data blocks in the database file so
     * that there are no (or as few as possible) unused blocks (gaps).
     * This is especially helpful after deleting point attributes or
     * calling finalize().
     *
     * \note This might be a lengthy operation and no other client can
     *       access the database in the meantime, not even to read.
     *}
    procedure Vacuum(
      Progress : TRDBProgressFunction = nil; //!< [in] vacuum progress callback function
      Userdata : Pointer              = nil  //!< [in] progress callback function user data
    ); overload;
    procedure Vacuum(
      Progress : TRDBProgressMethod //!< [in] vacuum progress callback method
    ); overload;

    (*!
     * \brief Validate database file
     *
     * This function checks whether the database corresponds to the given schema.
     * The schema contains a list of required and optional point attributes and
     * metadata entries and is given in JSON format. Primary point attributes
     * are marked with a "*", optional attributes or metadata entries are
     * marked with a "?" appended to the name, all other items are required.
     *
     * The database must at least contain all primary and required point attributes
     * and all required metadata entries to correspond to the schema. If "strict"
     * is "true", then the database additionally is not allowed to contain extra
     * point attributes or metadata entries that are not listed in the schema.
     *
     * If the database does not correspond to the schema, an exception
     * is thrown and the reason can be found in the exception details.
     *
     * Example schema JSON string:
     *
     *     {
     *         "extension": "rdbx",
     *         "attributes": [
     *             "riegl.xyz*",
     *             "riegl.timestamp",
     *             "riegl.class?"
     *         ],
     *         "metadata": [
     *             "riegl.geo_tag",
     *             "riegl.device?"
     *         ]
     *     }
     *)
    procedure Validate(
      const Schema : String; //!< [in] database schema (JSON)
      const Strict : Boolean //!< [in] true: strict mode
    );

  private
    FContext    : TRDBContext;
    FPointcloud : TRDBPointcloudHandle;
  end;

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

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

uses
  Math,
  SysUtils,
  riegl.rdb.pointcloud;

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

type
  TFunctionProgressWrapper = class(TObject)
  public
    ProgressUserdata : Pointer;
    ProgressFunction : TRDBProgressFunction;
    ProgressMethod   : TRDBProgressMethod;

    constructor Create; reintroduce;
    procedure Execute(Progress : TRDBUInt8);
  end;

  constructor TFunctionProgressWrapper.Create;
  begin
    inherited Create;
    ProgressUserdata := nil;
    ProgressFunction := nil;
    ProgressMethod   := nil;
  end;

  procedure TFunctionProgressWrapper.Execute(Progress : TRDBUInt8);
  begin
    if Assigned(ProgressFunction) then
    begin
      ProgressFunction(Progress, ProgressUserdata);
    end
    else if Assigned(ProgressMethod) then
    begin
      ProgressMethod(Progress);
    end;
  end;

  procedure FunctionProgressWrapperExecute(Progress : TRDBUInt8; Userdata : Pointer) cdecl;
  begin
    try
      TFunctionProgressWrapper(Userdata).Execute(Progress);
    except
      on E:Exception do; // ignore all errors
    end;
  end;

//---< TRDBPointcloudManagement::PUBLIC >---------------------------------------

constructor TRDBPointcloudManagement.Create(Parent : System.TObject);
begin
  inherited Create;
  FContext    := (Parent as TRDBPointcloud).Context;
  FPointcloud := (Parent as TRDBPointcloud).Pointcloud;
end; // Create()

destructor TRDBPointcloudManagement.Destroy;
begin
  inherited;
end; // Destroy()

function TRDBPointcloudManagement.GetLodMode() : TRDBPointcloudLodMode;
var
  Value : TRDBUInt32;
begin
  Value := 0;
  FContext.Check(rdb_pointcloud_management_get_lod_mode(
    FContext.Handle, FPointcloud, Value
  ));
  Result := TRDBPointcloudLodMode(Value);
end; // GetLodMode()

procedure TRDBPointcloudManagement.SetLodMode(const Value : TRDBPointcloudLodMode);
begin
  FContext.Check(rdb_pointcloud_management_set_lod_mode(
    FContext.Handle, FPointcloud, Ord(Value)
  ));
end; // SetLodMode()

function TRDBPointcloudManagement.GetChunkSizeLOD() : TRDBUInt32;
begin
  Result := 0;
  FContext.Check(rdb_pointcloud_management_get_chunk_size_lod(
    FContext.Handle, FPointcloud, Result
  ));
end; // GetChunkSizeLOD()

procedure TRDBPointcloudManagement.SetChunkSizeLOD(const Value : TRDBUInt32);
begin
  FContext.Check(rdb_pointcloud_management_set_chunk_size_lod(
    FContext.Handle, FPointcloud, Value
  ));
end; // SetChunkSizeLOD()

procedure TRDBPointcloudManagement.Finalize();
begin
  FContext.Check(rdb_pointcloud_management_finalize(
    FContext.Handle, FPointcloud
  ));
end; // Finalize()

procedure TRDBPointcloudManagement.Vacuum(
  Progress : TRDBProgressFunction;
  Userdata : Pointer
);
var
  Wrapper : TFunctionProgressWrapper;
begin
  Wrapper := TFunctionProgressWrapper.Create;
  try
    Wrapper.ProgressUserdata := Userdata;
    Wrapper.ProgressFunction := Progress;
    FContext.Check(rdb_pointcloud_management_vacuum(
      FContext.Handle, FPointcloud, FunctionProgressWrapperExecute, Wrapper
    ));
  finally
    FreeAndNil(Wrapper);
  end; // try..finally
end; // Vacuum()

procedure TRDBPointcloudManagement.Vacuum(Progress : TRDBProgressMethod);
var
  Wrapper : TFunctionProgressWrapper;
begin
  Wrapper := TFunctionProgressWrapper.Create;
  try
    Wrapper.ProgressMethod := Progress;
    FContext.Check(rdb_pointcloud_management_vacuum(
      FContext.Handle, FPointcloud, FunctionProgressWrapperExecute, Wrapper
    ));
  finally
    FreeAndNil(Wrapper);
  end; // try..finally
end; // Vacuum()

procedure TRDBPointcloudManagement.Validate(
  const Schema : String;
  const Strict : Boolean
);
begin
  FContext.Check(rdb_pointcloud_management_validate(
    FContext.Handle, FPointcloud,
    TRDBString(AnsiToUtf8(Schema)),
    IfThen(Strict, 1, 0)
  ));
end;

end.
