/*
 *******************************************************************************
 *
 *  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    pointcloud.cpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Main point cloud database class (C++ 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-14/AW: New "invert query" (class QueryInvert) added (#2406)
 * \version 2016-11-16/AW: New function to output database statistics added
 * \version 2017-08-22/AW: Added function to query database UUID (#2720)
 * \version 2018-05-28/AW: New variant of create() 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-06-29/AW: Database changelog interface added (#3614)
 * \version 2021-05-07/AW: Class Pointcloud is default constructible (#3887)
 *
 *******************************************************************************
 */

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

#include <cstdlib>

#include "riegl/rdb.h"
#include "riegl/rdb.hpp"

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

namespace riegl {
namespace rdb {

//---< Pointcloud::PUBLIC >-----------------------------------------------------

//---< Constructors and destructor >--------------------------------------------

Pointcloud::Pointcloud(Context context):
    data(std::make_shared<PointcloudData>(std::move(context)))
{
} // constructor

Pointcloud::~Pointcloud()
{
} // destructor

//---< Database Access >--------------------------------------------------------

void Pointcloud::create(
    const std::string                &location,
    const pointcloud::CreateSettings &settings
)
{
    pointcloud::CreateSettingsWrapper::post(settings);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_create(
        data->contextHandle(), data->pointcloud, location.c_str(),
        pointcloud::CreateSettingsWrapper::handle(settings)
    ));
} // create()

void Pointcloud::create(
    const std::string                &location,
    const pointcloud::CreateSettings &settings,
    const std::string                &schema,
    const bool                        optionals
)
{
    pointcloud::CreateSettingsWrapper::post(settings);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_create_with_schema(
        data->contextHandle(), data->pointcloud, location.c_str(),
        pointcloud::CreateSettingsWrapper::handle(settings),
        schema.c_str(), optionals ? 1 : 0
    ));
} // create()

void Pointcloud::open(
    const std::string              &location,
    const pointcloud::OpenSettings &settings
)
{
    pointcloud::OpenSettingsWrapper::post(settings);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_open(
        data->contextHandle(), data->pointcloud, location.c_str(),
        pointcloud::OpenSettingsWrapper::handle(settings)
    ));
} // open()

void Pointcloud::close()
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_close(
        data->contextHandle(), data->pointcloud
    ));
} // close()

bool Pointcloud::isOpen() const
{
    std::uint32_t result(0);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_is_open(
        data->contextHandle(), data->pointcloud, &result
    ));
    return (result != 0);
} // isOpen()

bool Pointcloud::isEmpty() const
{
    std::uint32_t result(0);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_is_empty(
        data->contextHandle(), data->pointcloud, &result
    ));
    return (result != 0);
} // isEmpty()

std::string Pointcloud::getUUID() const
{
    RDBString result(nullptr);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_get_uuid(
        data->contextHandle(), data->pointcloud, &result
    ));
    return std::string(result ? result : "");
}

std::string Pointcloud::inspect(const std::uint8_t format)
{
    RDBString result(nullptr);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_inspect(
        data->contextHandle(), data->pointcloud, format, &result
    ));
    return std::string(result ? result : "");
} // inspect()

void Pointcloud::clearCache()
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_clear_cache(
        data->contextHandle(), data->pointcloud
    ));
} // clearCache()

//---< Database Management >----------------------------------------------------

pointcloud::Management &Pointcloud::management()
{
    return *(data->management);
} // management()

const pointcloud::Management &Pointcloud::management() const
{
    return *(data->management);
} // management()

pointcloud::Changelog &Pointcloud::changelog()
{
    return *(data->changelog);
} // changelog()

const pointcloud::Changelog &Pointcloud::changelog() const
{
    return *(data->changelog);
} // changelog()

pointcloud::MetaData &Pointcloud::metaData()
{
    return *(data->metaData);
} // metaData()

const pointcloud::MetaData &Pointcloud::metaData() const
{
    return *(data->metaData);
} // metaData()

pointcloud::PointAttributes &Pointcloud::pointAttribute()
{
    return *(data->pointAttributes);
} // pointAttribute()

const pointcloud::PointAttributes &Pointcloud::pointAttribute() const
{
    return *(data->pointAttributes);
} // pointAttribute()

pointcloud::Transactions &Pointcloud::transaction()
{
    return *(data->transactions);
} // transaction()

const pointcloud::Transactions &Pointcloud::transaction() const
{
    return *(data->transactions);
} // transaction()

//---< Point Queries >----------------------------------------------------------

namespace { typedef std::vector<pointcloud::GraphNode::ID> NodeList; }

pointcloud::QueryInsert Pointcloud::insert()
{
    return pointcloud::QueryInsert(data.get());
} // insert()

pointcloud::QueryUpdate Pointcloud::update()
{
    return pointcloud::QueryUpdate(data.get());
} // update()

pointcloud::QuerySelect Pointcloud::select(const std::string &filter)
{
    return pointcloud::QuerySelect(data.get(), static_cast<NodeList*>(nullptr), filter);
} // select()

pointcloud::QuerySelect Pointcloud::select(
    const pointcloud::GraphNode::ID &node,
    const std::string &filter
)
{
    if (node != 0)
    {
        const NodeList nodes(&node, &node+1);
        return pointcloud::QuerySelect(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QuerySelect(); // = empty/invalid query
    }
} // select()

pointcloud::QuerySelect Pointcloud::select(
    const std::vector<pointcloud::GraphNode::ID> &nodes,
    const std::string &filter
)
{
    if (!nodes.empty())
    {
        return pointcloud::QuerySelect(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QuerySelect(); // = empty/invalid query
    }
} // select()

pointcloud::QueryFill Pointcloud::fill(const std::string &filter)
{
    return pointcloud::QueryFill(data.get(), static_cast<NodeList*>(nullptr), filter);
} // fill()

pointcloud::QueryFill Pointcloud::fill(
    const pointcloud::GraphNode::ID &node,
    const std::string &filter
)
{
    if (node != 0)
    {
        const NodeList nodes(&node, &node+1);
        return pointcloud::QueryFill(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QueryFill(); // = empty/invalid query
    }
} // fill()

pointcloud::QueryFill Pointcloud::fill(
    const std::vector<pointcloud::GraphNode::ID> &nodes,
    const std::string &filter
)
{
    if (!nodes.empty())
    {
        return pointcloud::QueryFill(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QueryFill(); // = empty/invalid query
    }
} // fill()

pointcloud::QueryInvert Pointcloud::invert(const std::string &filter)
{
    return pointcloud::QueryInvert(data.get(), static_cast<NodeList*>(nullptr), filter);
} // invert()

pointcloud::QueryInvert Pointcloud::invert(
    const pointcloud::GraphNode::ID &node,
    const std::string &filter
)
{
    if (node != 0)
    {
        const NodeList nodes(&node, &node+1);
        return pointcloud::QueryInvert(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QueryInvert(); // = empty/invalid query
    }
} // invert()

pointcloud::QueryInvert Pointcloud::invert(
    const std::vector<pointcloud::GraphNode::ID> &nodes,
    const std::string &filter
)
{
    if (!nodes.empty())
    {
        return pointcloud::QueryInvert(data.get(), &nodes, filter);
    }
    else
    {
        return pointcloud::QueryInvert(); // = empty/invalid query
    }
} // invert()

pointcloud::QueryRemove Pointcloud::remove()
{
    return pointcloud::QueryRemove(data.get());
} // remove()

pointcloud::QueryStat Pointcloud::stat()
{
    return pointcloud::QueryStat(data.get());
} // stat()

}} // namespace riegl::rdb
