/*
 *******************************************************************************
 *
 *  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    pointAttributes.cpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Manage point attributes (C++ wrapper code)
 * \version 2015-10-14/AW: Initial version
 * \version 2016-01-04/AW: Added function to duplicate point attribute data
 * \version 2016-01-11/AW: Added function to discard point attribute data
 * \version 2017-03-23/AW: Added function to modify point attribute details
 * \version 2017-11-09/AW: Added function to merge point attribute details
 * \version 2019-02-15/AW: Fix C++ API wrapper of PointAttribute class
 * \version 2019-04-15/AW: Added function to query point attribute group (#3342)
 * \version 2022-09-20/AW: Added function to query attributes of filter (#4349)
 * \version 2023-05-04/AW: Improve PointAttributes::list*() functions (#4569)
 *
 *******************************************************************************
 */

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

#include <cstdlib>

#include "riegl/rdb.h"
#include "riegl/rdb.hpp"
#include "riegl/rdb/pointcloud/dataTypes.inc"

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

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< PointAttributes::PUBLIC >------------------------------------------------

PointAttributes::PointAttributes(riegl::rdb::PointcloudData* pointcloud):
    data(pointcloud)
{
}

std::vector<std::string> PointAttributes::list() const
{
    std::uint32_t count(0); RDBString list(nullptr);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_list(
        data->contextHandle(), data->pointcloud, &count, &list
    ));
    std::vector<std::string> result;
    result.reserve(count);
    for (std::uint32_t i = 0; i < count; i++)
    {
        result.emplace_back(list);
        list += result[i].size()+1;
    }
    return result;
}

std::vector<std::string> PointAttributes::listDefault(riegl::rdb::Context &context)
{
    std::uint32_t count(0); RDBString list(nullptr);
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_list_default(
        context.data->context, &count, &list
    ));
    std::vector<std::string> result;
    result.reserve(count);
    for (std::uint32_t i = 0; i < count; i++)
    {
        result.emplace_back(list);
        list += result[i].size()+1;
    }
    return result;
}

std::vector<std::string> PointAttributes::listFiltered(
    const std::string   &filter,
    riegl::rdb::Context &context
)
{
    std::uint32_t count(0); RDBString list(nullptr);
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_list_filtered(
        context.data->context, filter.c_str(), &count, &list
    ));
    std::vector<std::string> result;
    result.reserve(count);
    for (std::uint32_t i = 0; i < count; i++)
    {
        result.emplace_back(list);
        list += result[i].size()+1;
    }
    return result;
}

bool PointAttributes::exists(const std::string &name) const
{
    std::uint32_t result(0);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_exists(
        data->contextHandle(), data->pointcloud, name.c_str(), &result
    ));
    return (result != 0);
}

void PointAttributes::add(const PointAttribute &attribute)
{
    const PointAttributeWrapper::View view(attribute, false);
    PointAttributeWrapper::post(attribute);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_add(
        data->contextHandle(), data->pointcloud, view.hnd
    ));
}

void PointAttributes::add(const std::string &name)
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_add_default(
        data->contextHandle(), data->pointcloud, name.c_str()
    ));
}

PointAttribute PointAttributes::get(const std::string &name) const
{
    PointAttribute result;
    const PointAttributeWrapper::View view(result, true);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_get(
        data->contextHandle(), data->pointcloud, name.c_str(), view.hnd
    ));
    PointAttributeWrapper::read(result);
    return result;
}

void PointAttributes::group(
    const std::string &name,
    std::string       &group,
    std::uint32_t     &index
) const
{
    RDBString result(nullptr); index = 0;
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_group(
        data->contextHandle(), data->pointcloud,
        name.c_str(), &result, &index
    ));
    group = std::string(result);
}

void PointAttributes::put(const PointAttribute &attribute)
{
    const PointAttributeWrapper::View view(attribute, false);
    PointAttributeWrapper::post(attribute);
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_put(
        data->contextHandle(), data->pointcloud, view.hnd
    ));
}

PointAttribute PointAttributes::getDefault(
    riegl::rdb::Context &context,
    const std::string   &name
)
{
    PointAttribute result(context);
    const PointAttributeWrapper::View view(result, true);
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_get_default(
        context.data->context, name.c_str(), view.hnd
    ));
    PointAttributeWrapper::read(result);
    return result;
}

void PointAttributes::groupDefault(
    riegl::rdb::Context &context,
    const std::string   &name,
    std::string         &group,
    std::uint32_t       &index
)
{
    RDBString result(nullptr); index = 0;
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_group_default(
        context.data->context, name.c_str(), &result, &index
    ));
    group = std::string(result ? result : "");
}

PointAttribute PointAttributes::getMerged(
    riegl::rdb::Context                        &context,
    const std::vector<riegl::rdb::Pointcloud*> &pointclouds,
    const std::string                          &name
)
{
    PointAttribute result;
    const PointAttributeWrapper::View view(result, true);
    std::vector<RDBPointcloud*> list; list.reserve(pointclouds.size());
    for (auto it = pointclouds.begin(); it != pointclouds.end(); ++it)
    {
        list.push_back((*it)->data->pointcloud); // list of handles
    }
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_get_merged(
        context.data->context,
        list.data(),
        static_cast<std::uint32_t>(list.size()),
        name.c_str(),
        view.hnd
    ));
    PointAttributeWrapper::read(result);
    return result;
}

std::vector<PointAttribute> PointAttributes::getMerged(
    riegl::rdb::Context                        &context,
    const std::vector<riegl::rdb::Pointcloud*> &pointclouds
)
{
    std::vector<RDBPointcloud*>               pointcloud_list;
    std::vector<PointAttribute>               attribute_data;
    std::vector<PointAttributeWrapper::View>  attribute_view;
    std::vector<RDBPointcloudPointAttribute*> attribute_list;

    // prepare point cloud handle array
    pointcloud_list.reserve(pointclouds.size());
    for (auto it = pointclouds.begin(); it != pointclouds.end(); ++it)
    {
        pointcloud_list.push_back((*it)->data->pointcloud);
    }

    // query number of attributes
    std::uint32_t attribute_size(0);
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_get_merged_all(
        context.data->context,
        pointcloud_list.data(), static_cast<std::uint32_t>(pointcloud_list.size()),
        attribute_list .data(), &attribute_size
    ));

    // prepare point attribute data and handle arrays
    attribute_data.reserve(attribute_size);
    attribute_view.reserve(attribute_size);
    attribute_list.reserve(attribute_size);
    for (std::uint32_t i = 0; i < attribute_size; ++i)
    {
        attribute_data.emplace_back(PointAttribute());
        attribute_view.emplace_back(PointAttributeWrapper::View(attribute_data.back(), true));
        attribute_list.emplace_back(attribute_view.back().hnd);
    }

    // query merged point attributes
    ErrorImpl::check(context.data->context, rdb_pointcloud_point_attributes_get_merged_all(
        context.data->context,
        pointcloud_list.data(), static_cast<std::uint32_t>(pointcloud_list.size()),
        attribute_list .data(), &attribute_size
    ));

    // read back results
    for (auto it = attribute_data.begin(); it != attribute_data.end(); ++it)
    {
        PointAttributeWrapper::read(*it);
    }
    return attribute_data;
}

void PointAttributes::remove(const std::string &name)
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_remove(
        data->contextHandle(), data->pointcloud, name.c_str()
    ));
}

void PointAttributes::duplicate(const std::string &source, const std::string &target)
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_duplicate(
        data->contextHandle(), data->pointcloud, source.c_str(), target.c_str()
    ));
}

void PointAttributes::discard(const std::string &name)
{
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_discard(
        data->contextHandle(), data->pointcloud, name.c_str()
    ));
}

std::string PointAttributes::pointIDName()
{
    RDBString result;
    rdb_pointcloud_point_attributes_point_id_name(&result);
    return std::string(result);
} // pointIDName()

std::string PointAttributes::pointIDUnit()
{
    RDBString result;
    rdb_pointcloud_point_attributes_point_id_unit(&result);
    return std::string(result);
} // pointIDUnit()

DataType PointAttributes::pointIDType()
{
    std::uint32_t result;
    rdb_pointcloud_point_attributes_point_id_type(&result);
    return convertDataType(result);
} // pointIDType()

std::string PointAttributes::primaryAttributeName() const
{
    RDBString result;
    ErrorImpl::check(data->contextHandle(), rdb_pointcloud_point_attributes_primary_attribute_name(
        data->contextHandle(), data->pointcloud, &result
    ));
    return std::string(result);
} // primaryAttributeName()

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