{*
 *******************************************************************************
 *
 *  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.metaData.pas
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Manage point cloud meta data (Pascal wrapper code)
 * \version 2015-10-14/AW: Initial version
 * \version 2020-03-30/AW: New functions for metadata signatures (#3570)
 *
 * This class allows to manage database-wide properties (aka. "meta-data").
 * Arbitrary properties can be stored in key-value manner along with the
 * point cloud in the database. This might be used for example to store
 * comments, operator/software name or georeferencing information.
 *
 * \see riegl::rdb::Pointcloud::metaData()
 *
 *******************************************************************************
 *}

unit riegl.rdb.pointcloud.metaData;

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

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

uses
  Classes,
  riegl.rdb,
  riegl.rdb.context;

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

//______________________________________________________________________________
{*!
 * \brief Query property names
 *
 * \returns a list of property names
 *}
function rdb_pointcloud_meta_data_list(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  var Count  : TRDBUInt32;           //!< [out] number of names
  var List   : TRDBString            //!< [out] list of names, separated by \0
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Check if property exists
 *
 * \note Property names are case sensitive (i.e. "name" and "NAME" are different properties).
 * \returns true if a property with given name exists
 *}
function rdb_pointcloud_meta_data_exists(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString;           //!< [in] property name
  var Exists : TRDBUInt32            //!< [out] 0 if property does not exist
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Set property value
 *}
function rdb_pointcloud_meta_data_set(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString;           //!< [in] property name
  Value      : TRDBString            //!< [in] property value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Get property value
 *
 * If the given property name could not be found, the function returns
 * the given default value.
 *}
function rdb_pointcloud_meta_data_get(
  Context      : TRDBContextHandle;    //!< [in] library context
  Pointcloud   : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name         : TRDBString;           //!< [in] property name
  var Value    : TRDBString;           //!< [out] property value
  DefaultValue : TRDBString = nil      //!< [in] default value
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Delete property
 *
 * If the given property name could not be found, this function does not fail.
 *}
function rdb_pointcloud_meta_data_remove(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString            //!< [in] property name
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Validate property value
 *
 * This function validates the property given by 'name' against the
 * corresponding built-in schema for RIEGL default metadata entries.
 *
 * If the value does not correspond to the schema, an exception
 * is thrown and the reason can be found in the exception details.
 *}
function rdb_pointcloud_meta_data_validate(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString            //!< [in] name of property to be validated
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Validate value
 *
 * This function validates the given JSON value against the JSON schema.
 *
 * If the value does not correspond to the schema, an exception
 * is thrown and the reason can be found in the exception details.
 *}
function rdb_pointcloud_meta_data_validate_json(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Value      : TRDBString;           //!< [in] value to be validated
  Schema     : TRDBString            //!< [in] schema to be validated against
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Create a signature for a metadata entry.
 *
 * The signature is stored next to the metadata entry in the database file
 * and cannot be read out or modified. A transaction must be started first.
 *
 * Set 'Method' to 0 to delete an existing signature ('KeySize' and
 * 'KeyData' are ignored in this case).
 *
 * \since 2.3.0
 *}
function rdb_pointcloud_meta_data_create_signature(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString;           //!< [in] name of metadata entry to sign
  Method     : TRDBUInt32;           //!< [in] signature method (0: delete, 1: default)
  KeySize    : TRDBUInt32;           //!< [in] signature encryption key size (at least 32 byte)
  KeyData    : Pointer               //!< [in] signature encryption key buffer
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Verify the signature of a metadata entry.
 *
 * Returns 'false' (0) if:
 *
 * - there is no signature for the metadata entry
 * - the signature does not match the current value
 * - a wrong signature encryption key was given
 *
 * Otherwise returns 'true' (1).
 *
 * Set 'Method' to 0 to check if a signature exists, no matter if it is
 * valid or not ('KeySize' and 'KeyData' are ignored in this case).
 *
 * \since 2.3.0
 *}
function rdb_pointcloud_meta_data_verify_signature(
  Context    : TRDBContextHandle;    //!< [in] library context
  Pointcloud : TRDBPointcloudHandle; //!< [in] point cloud handle
  Name       : TRDBString;           //!< [in] name of metadata entry to verify
  Method     : TRDBUInt32;           //!< [in] signature method (0: exists, 1: default)
  KeySize    : TRDBUInt32;           //!< [in] signature encryption key size (at least 32 byte)
  KeyData    : Pointer;              //!< [in] signature encryption key buffer
  var Valid  : TRDBUInt32            //!< [out] 0 if the signature is not valid
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//---< CLASS TRDBPointcloudMetaData >-------------------------------------------
{*!
 * \brief Manage point cloud meta data
 *
 * This class allows to manage database-wide properties (aka. "meta-data").
 * Arbitrary properties can be stored in key-value manner along with the
 * point cloud in the database. This might be used for example to store
 * comments, operator/software name or georeferencing information.
 *
 * \see riegl::rdb::Pointcloud::metaData()
 *}
type
  TRDBPointcloudMetaData = class(System.TObject)
  public
    {*!
     * \brief Constructor
     * \note  You cannot create new TRDBPointcloudMetaData objects directly,
     *        use riegl::rdb::Pointcloud::metaData() instead.
     *}
    constructor Create(Parent : System.TObject); reintroduce;
    destructor  Destroy; override;

    {*!
     * \brief Query property names
     *
     * \returns a list of property names
     *}
    function List() : Classes.TStringList;

    {*!
     * \brief Check if property exists
     *
     * \note Property names are case sensitive (i.e. "name" and "NAME" are different properties).
     * \returns true if a property with given name exists
     *}
    function Exists(
      const Name : System.String //!< [in] property name
    ) : System.Boolean;

    {*!
     * \brief Set property value
     *}
    procedure SetValue(
      const Name  : System.String; //!< [in] property name
      const Value : System.String  //!< [in] property value
    );

    {*!
     * \brief Get property value
     *
     * If the given property name could not be found, the function returns
     * the given default value.
     *}
    function GetValue(
      const Name         : System.String;     //!< [in] property name
      const DefaultValue : System.String = '' //!< [in] default value
    ) : String;

    {*!
     * \brief Delete property
     *
     * If the given property name could not be found, this function does not fail.
     *}
    procedure Remove(
      const Name : System.String //!< [in] property name
    );

    {*!
     * \brief Validate property value
     *
     * This function validates the property given by 'name' against the
     * corresponding built-in schema for RIEGL default metadata entries.
     *
     * If the value does not correspond to the schema, an exception
     * is thrown and the reason can be found in the exception details.
     *}
    procedure Validate(
      const Name : System.String //!< [in] name of property to be validated
    ); overload;

    {*!
     * \brief Validate value
     *
     * This function validates the given JSON value against the JSON schema.
     *
     * If the value does not correspond to the schema, an exception
     * is thrown and the reason can be found in the exception details.
     *}
    procedure Validate(
      const Value  : System.String; //!< [in] value to be validated
      const Schema : System.String  //!< [in] schema to be validated against
    ); overload;

    {*!
     * \brief Create a signature for a metadata entry.
     *
     * The signature is stored next to the metadata entry in the database file
     * and cannot be read out or modified. A transaction must be started first.
     *
     * Set 'Method' to 0 to delete an existing signature ('KeySize' and
     * 'KeyData' are ignored in this case).
     *
     * \since 2.3.0
     *}
    procedure CreateSignature(
      const Name    : System.String; //!< [in] name of metadata entry to sign
      const Method  : TRDBUInt32;    //!< [in] signature method (0: delete, 1: default)
      const KeySize : TRDBUInt32;    //!< [in] signature encryption key size (at least 32 byte)
      const KeyData : Pointer        //!< [in] signature encryption key buffer
    );

    {*!
     * \brief Verify the signature of a metadata entry.
     *
     * Returns 'false' if:
     *
     * - there is no signature for the metadata entry
     * - the signature does not match the current value
     * - a wrong signature encryption key was given
     *
     * Otherwise returns 'true'.
     *
     * Set 'Method' to 0 to check if a signature exists, no matter if it is
     * valid or not ('KeySize' and 'KeyData' are ignored in this case).
     *
     * \since 2.3.0
     *}
    function VerifySignature(
      const Name    : System.String; //!< [in] name of metadata entry to verify
      const Method  : TRDBUInt32;    //!< [in] signature method (0: exists, 1: default)
      const KeySize : TRDBUInt32;    //!< [in] signature encryption key size (at least 32 byte)
      const KeyData : Pointer        //!< [in] signature encryption key buffer
    ) : Boolean;

  private
    FContext    : TRDBContext;
    FPointcloud : TRDBPointcloudHandle;
  end;

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

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

uses
  riegl.rdb.pointcloud;

//---< TRDBPointcloudMetaData::PUBLIC >-----------------------------------------

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

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

function TRDBPointcloudMetaData.List() : Classes.TStringList;
var
  i     : Integer;
  Count : TRDBUInt32;
  List  : TRDBString;
  Value : String;
begin
  Count := 0; List := nil;
  FContext.Check(rdb_pointcloud_meta_data_list(
    FContext.Handle, FPointcloud, Count, List
  ));
  Result := Classes.TStringList.Create;
  if (Count > 0) then for i := 0 to Count-1 do
  begin
    Value := AsSTDString(List);
    Result.Add(Value);
    Inc(List, Length(Value)+1);
  end;
end; // List()

function TRDBPointcloudMetaData.Exists(const Name : System.String) : System.Boolean;
var
  Value : TRDBUInt32;
begin
  FContext.Check(rdb_pointcloud_meta_data_exists(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Name)), Value
  ));
  Result := (Value <> 0);
end; // Exists()

procedure TRDBPointcloudMetaData.SetValue(const Name : System.String; const Value : System.String);
begin
  FContext.Check(rdb_pointcloud_meta_data_set(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Name)), TRDBString(AnsiToUtf8(Value))
  ));
end;// SetValue()

function TRDBPointcloudMetaData.GetValue(const Name :System.String; const DefaultValue : System.String) : String;
var
  Value : TRDBString;
begin
  FContext.Check(rdb_pointcloud_meta_data_get(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Name)), Value, TRDBString(AnsiToUtf8(DefaultValue))
  ));
  Result := AsSTDString(Value);
end; // GetValue()

procedure TRDBPointcloudMetaData.Remove(const Name : System.String);
begin
  FContext.Check(rdb_pointcloud_meta_data_remove(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Name))
  ));
end; // Remove()

procedure TRDBPointcloudMetaData.Validate(const Name : System.String);
begin
  FContext.Check(rdb_pointcloud_meta_data_validate(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Name))
  ));
end; // Validate()

procedure TRDBPointcloudMetaData.Validate(const Value : System.String; const Schema : System.String);
begin
  FContext.Check(rdb_pointcloud_meta_data_validate_json(
    FContext.Handle, FPointcloud, TRDBString(AnsiToUtf8(Value)), TRDBString(AnsiToUtf8(Schema))
  ));
end; // Validate()

procedure TRDBPointcloudMetaData.CreateSignature(
  const Name    : System.String;
  const Method  : TRDBUInt32;
  const KeySize : TRDBUInt32;
  const KeyData : Pointer
);
begin
  FContext.Check(rdb_pointcloud_meta_data_create_signature(
    FContext.Handle, FPointcloud,
    TRDBString(AnsiToUtf8(Name)),
    Method, KeySize, KeyData
  ));
end; // CreateSignature()

function TRDBPointcloudMetaData.VerifySignature(
  const Name    : System.String;
  const Method  : TRDBUInt32;
  const KeySize : TRDBUInt32;
  const KeyData : Pointer
) : Boolean;
var
  Valid : TRDBUInt32;
begin
  FContext.Check(rdb_pointcloud_meta_data_verify_signature(
    FContext.Handle, FPointcloud,
    TRDBString(AnsiToUtf8(Name)),
    Method, KeySize, KeyData,
    Valid
  ));
  Result := (Valid <> 0);
end; // VerifySignature()

end.
