/*
 *******************************************************************************
 *
 *  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    management.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Basic point cloud management interface
 * \version 2017-03-21/AW: Initial version
 * \version 2017-04-13/AW: Functions finalize() and vacuum() added
 * \version 2017-11-24/AW: Constructors declared as "explicit" (#2825)
 * \version 2018-05-25/AW: Function validate() added (#3109)
 * \version 2019-01-18/AW: Parameter 'lodMode' added
 * \version 2020-03-30/AW: Function validate() is const (#3579)
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_MANAGEMENT_HPP
#define RIEGL_RDB_POINTCLOUD_MANAGEMENT_HPP

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

#include <memory>
#include <string>
#include <vector>
#include <cstdlib>
#include <cstdint>

#include "riegl/rdb/pointcloud/pointcloudData.hpp"
#include "riegl/rdb/progress.hpp"

//---< NAMESPACE >--------------------------------------------------------------

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< CLASS Management >-------------------------------------------------------
/*!
 * \brief Basic point cloud management interface
 *
 * \see riegl::rdb::Pointcloud::management()
 */
class Management
{
public:
    /*!
     * \brief Constructor
     * \note  You cannot create new Management objects directly,
     *        use riegl::rdb::Pointcloud::management() instead.
     */
    explicit Management(riegl::rdb::PointcloudData* pointcloud);

    /*!
     * \brief Query level of detail mode
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     */
    std::uint32_t getLodMode() const;

    /*!
     * \brief Modify level of detail mode
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     */
    void setLodMode(const std::uint32_t value);

    /*!
     * \brief Query level of detail size
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     */
    std::uint32_t getChunkSizeLOD() const;

    /*!
     * \brief Modify level of detail size
     * \see riegl::rdb::pointcloud::CreateSettings::LodMode
     */
    void setChunkSizeLOD(const std::uint32_t value);

    /*!
     * \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().
     */
    void 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.
     */
    void vacuum(
        Progress progress = nullptr, //!< [in] vacuum progress callback function
        void    *userdata = nullptr  //!< [in] progress callback function user data
    );

    /*!
     * \brief Optimize database file
     *
     * Overloaded vacuum() that takes any callable type as progress callback.
     */
    template<typename Callable>
    void vacuum(Callable &&progress)
    {
        typedef typename std::decay<Callable>::type CallableType;
        this->vacuum(
            &progress_proxy_callable<CallableType>,
            const_cast<CallableType*>(&progress)
        );
    }

    /*!
     * \brief Optimize database file
     *
     * Overloaded vacuum() that takes a progress callback method of an object.
     */
    template<typename Receiver>
    void vacuum(void (Receiver::*progress)(std::uint8_t), Receiver &receiver)
    {
        auto userdata = std::make_pair(progress, &receiver);
        this->vacuum(&progress_proxy_receiver<Receiver>, &userdata);
    }

    /*!
     * \brief Optimize database file
     *
     * Overloaded vacuum() that takes a constant progress callback method
     * of a constant object.
     */
    template<typename Receiver>
    void vacuum(void (Receiver::*progress)(std::uint8_t) const, const Receiver &receiver)
    {
        auto userdata = std::make_pair(progress, &receiver);
        this->vacuum(&progress_proxy_receiver<Receiver>, &userdata);
    }

    /*!
     * \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?"
     *         ]
     *     }
     */
    void validate(
        const std::string &schema,      //!< [in] database schema (JSON)
        const bool         strict=false //!< [in] true: strict mode
    ) const;

private:
    riegl::rdb::PointcloudData *data;

    template<typename Callable>
    static void progress_proxy_callable(std::uint8_t progress, void *userdata)
    {
        try
        {
            Callable &callback = *reinterpret_cast<Callable*>(userdata);
            callback(progress); // = invoke original callback
        }
        catch(...)
        {
            // ignore all errors
        }
    }

    template<typename Receiver>
    static void progress_proxy_receiver(std::uint8_t progress, void *userdata)
    {
        try
        {
            typedef void (Receiver::*Function)(std::uint8_t); // just a shortcut...
            auto data(reinterpret_cast<std::pair<Function, Receiver*>*>(userdata));
            (*data->second.*data->first)(progress); // = invoke original callback
        }
        catch(...)
        {
            // ignore all errors
        }
    }
};

}}} // namespace riegl::rdb::pointcloud

#endif // RIEGL_RDB_POINTCLOUD_MANAGEMENT_HPP
