{*
 *******************************************************************************
 *
 *  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.queryInvert.pas
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point invert query (Pascal wrapper code)
 * \version 2016-11-21/AW: Initial version
 *
 * This class can be used to invert point attributes for all points or just
 * those that meet some filter criteria. Calculating the inverted value (v')
 * from the original value (v) and the attribute limits (min and max) is done
 * as follows:
 *
 *   v' = max - (v - min)
 *
 * For dynamic point attributes (bits/flags) this simply means to flip the
 * bit (e.g. set "riegl.selected" to 1 if it was 0, otherwise set it to 0).
 *
 * This query is similar to using select() and update() and manually inverting
 * the point attributes except that it might be faster (less processing time)
 * and easier to use.
 *
 * \see riegl::rdb::Pointcloud::invert()
 *
 *******************************************************************************
 *}

unit riegl.rdb.pointcloud.queryInvert;

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

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

uses
  riegl.rdb,
  riegl.rdb.context,
  riegl.rdb.pointcloud.graphNode,
  riegl.rdb.pointcloud.dataTypes;

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

//______________________________________________________________________________
{*!
 * \brief Constructor
 * \see   riegl::rdb::Pointcloud::invert()
 *}
function rdb_pointcloud_query_invert_new(
  Context    : TRDBContextHandle;              //!< [in] library context
  Pointcloud : TRDBPointcloudHandle;           //!< [in] point cloud
  Node       : TRDBPointcloudGraphNodeID;      //!< [in] index node ID
  Filter     : TRDBString;                     //!< [in] filter string
  var Query  : TRDBPointcloudQueryInvertHandle //!< [out] query handle
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Constructor
 * \see   riegl::rdb::Pointcloud::invert()
 *}
function rdb_pointcloud_query_invert_nodes_new(
  Context    : TRDBContextHandle;              //!< [in] library context
  Pointcloud : TRDBPointcloudHandle;           //!< [in] point cloud
  Nodes      : PRDBPointcloudGraphNodeID;      //!< [in] pointer to first element of node ID array
  Count      : TRDBUInt32;                     //!< [in] number of elements in node ID array
  Filter     : TRDBString;                     //!< [in] filter string
  var Query  : TRDBPointcloudQueryInvertHandle //!< [out] query handle
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Destroy query instance
 *}
function rdb_pointcloud_query_invert_delete(
  Context   : TRDBContextHandle;              //!< [in] library context
  var Query : TRDBPointcloudQueryInvertHandle //!< [in] query handle
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Define attribute
 *
 * Use this function to define the point attribute to be inverted. To
 * define multiple attributes, simply call this function once for each.
 *
 * \note Point attributes having a length greater than 1 (vectors like
 *       the true color attribute "riegl.rgba") can only be inverted
 *       as a whole, i.e. you cannot invert particular vector elements
 *       (e.g. using "riegl.rgba[0]" will not work).
 *
 * \see riegl::rdb::pointcloud::PointAttributes
 *}
function rdb_pointcloud_query_invert_attribute(
  Context   : TRDBContextHandle;               //!< [in] library context
  Query     : TRDBPointcloudQueryInvertHandle; //!< [in] query handle
  Name      : TRDBString                       //!< [in] attribute name
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Invert points
 *
 * Use this function to actually invert the point attribute(s).
 *
 * To process all points, you need to repeatedly call next() until it
 * returns 0 (zero, see example 8). The number of points to process in
 * one step is defined by 'count'. Please note that the actual number
 * of processed points may be slightly different. To cancel processing
 * just stop calling next() and cancel (rollback) the transaction.
 *
 * \returns the number of points processed
 *}
function rdb_pointcloud_query_invert_next(
  Context   : TRDBContextHandle;               //!< [in] library context
  Query     : TRDBPointcloudQueryInvertHandle; //!< [in] query handle
  Count     : TRDBUInt32;                      //!< [in] size of source buffers in terms of points
  Processed : PRDBUInt32 = nil                 //!< [out] number of processed points (optional)
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//______________________________________________________________________________
{*!
 * \brief Progress
 *
 * This function returns a coarse progress information in percent (0..100%).
 * Since the total number of returned points is not known in advance, this
 * value just reflects the progress of the (internal) index traversal.
 *}
function rdb_pointcloud_query_invert_progress(
  Context  : TRDBContextHandle;               //!< [in] library context
  Query    : TRDBPointcloudQueryInvertHandle; //!< [in] query handle
  Progress : PRDBUInt32                       //!< [out] progress [0..100%]
) : TRDBResult; cdecl; external riegl.rdb.RDB_LIBRARY_FILENAME;

//---< CLASS TRDBPointcloudQueryInvert >----------------------------------------
{*!
 * This class can be used to invert point attributes for all points or just
 * those that meet some filter criteria. Calculating the inverted value (v')
 * from the original value (v) and the attribute limits (min and max) is done
 * as follows:
 *
 *   v' = max - (v - min)
 *
 * For dynamic point attributes (bits/flags) this simply means to flip the
 * bit (e.g. set "riegl.selected" to 1 if it was 0, otherwise set it to 0).
 *
 * This query is similar to using select() and update() and manually inverting
 * the point attributes except that it might be faster (less processing time)
 * and easier to use.
 *
 * \see riegl::rdb::Pointcloud::invert()
 *}
type
  TRDBPointcloudQueryInvert = class(System.TObject)
  public
    {*!
     * \brief Default constructor
     *
     * Creates a null query - i.e. the query cannot be used to modify points.
     *
     * \see riegl::rdb::Pointcloud::invert()
     *}
    constructor Create; reintroduce; overload;

    {*!
     * \brief Constructor
     * \note  You cannot create new TRDBPointcloudQueryInvert objects directly,
     *        use riegl::rdb::Pointcloud::invert() instead.
     *}
    constructor Create(
      Parent : System.TObject;
      Nodes  : PRDBPointcloudGraphNodeIDArray;
      Filter : String
    ); overload;

    destructor Destroy; override;

    {*!
     * \brief Define attribute
     *
     * Use this function to define the point attribute to be inverted. To
     * define multiple attributes, simply call this function once for each.
     *
     * \note Point attributes having a length greater than 1 (vectors like
     *       the true color attribute "riegl.rgba") can only be inverted
     *       as a whole, i.e. you cannot invert particular vector elements
     *       (e.g. using "riegl.rgba[0]" will not work).
     *
     * \see riegl::rdb::pointcloud::PointAttributes
     *}
    procedure Attribute(
      const Name : System.String //!< [in] attribute name
    );

    {*!
     * \brief Invert points
     *
     * Use this function to actually invert the point attribute(s).
     *
     * To process all points, you need to repeatedly call next() until it
     * returns 0 (zero, see example 8). The number of points to process in
     * one step is defined by 'count'. Please note that the actual number
     * of processed points may be slightly different. To cancel processing
     * just stop calling next() and cancel (rollback) the transaction.
     *
     * \returns the number of points processed
     *}
    function Next(
      const Count : TRDBUInt32 //!< [in] number of points to process
    ) : TRDBUInt32;

    {*!
     * \brief Progress
     *
     * This function returns a coarse progress information in percent (0..100%).
     * Since the total number of returned points is not known in advance, this
     * value just reflects the progress of the (internal) index traversal.
     *}
    function Progress() : TRDBUInt32;

  private
    FContext    : TRDBContext;
    FPointcloud : TRDBPointcloudHandle;
    FQuery      : TRDBPointcloudQueryInvertHandle;
  end;

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

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

uses
  riegl.rdb.pointcloud;

//---< TRDBPointcloudQueryInvert::PUBLIC >--------------------------------------

constructor TRDBPointcloudQueryInvert.Create;
begin
  FContext    := nil;
  FPointcloud := nil;
  FQuery      := nil;
end; // Create()

constructor TRDBPointcloudQueryInvert.Create(
  Parent : System.TObject;
  Nodes  : PRDBPointcloudGraphNodeIDArray;
  Filter : String
);
begin
  inherited Create;
  FQuery      := nil;
  FContext    := (Parent as TRDBPointcloud).Context;
  FPointcloud := (Parent as TRDBPointcloud).Pointcloud;
  //
  if (Nodes <> nil) then
  begin
    FContext.Check(rdb_pointcloud_query_invert_nodes_new(
      FContext.Handle, FPointcloud,
      @Nodes^[0], Length(Nodes^),
      TRDBString(AnsiToUtf8(Filter)),
      FQuery
    ));
  end
  else
  begin
    FContext.Check(rdb_pointcloud_query_invert_new(
      FContext.Handle, FPointcloud, 0, // = all nodes
      TRDBString(AnsiToUtf8(Filter)),
      FQuery
    ));
  end;
end; // Create()

destructor TRDBPointcloudQueryInvert.Destroy;
begin
  FContext.Check(rdb_pointcloud_query_invert_delete(FContext.Handle, FQuery));
  inherited;
end; // Destroy()

procedure TRDBPointcloudQueryInvert.Attribute(const Name : System.String);
begin
  if (FQuery <> nil) then
  begin
    FContext.Check(rdb_pointcloud_query_invert_attribute(
      FContext.Handle, FQuery, TRDBString(AnsiToUtf8(Name))
    ));
  end;
end; // Attribute()

function TRDBPointcloudQueryInvert.Next(const Count : TRDBUInt32) : TRDBUInt32;
begin
  if (FQuery <> nil) then
  begin
    FContext.Check(rdb_pointcloud_query_invert_next(
      FContext.Handle, FQuery, Count, @Result
    ));
  end
  else Result := 0;
end; // Next()

function TRDBPointcloudQueryInvert.Progress() : TRDBUInt32;
begin
  if (FQuery <> nil) then
  begin
    FContext.Check(rdb_pointcloud_query_invert_progress(
      FContext.Handle, FQuery, @Result
    ));
  end
  else Result := 0;
end; // Progress()

end.
