/*
 *******************************************************************************
 *
 *  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    dataTypes.hpp
 * \author  RIEGL LMS GmbH, Austria
 * \brief   Point attribute access data types
 * \version 2015-10-14/AW: Initial version
 * \version 2018-07-05/AW: Add tools to get data type enum and pointer of data
 *
 *******************************************************************************
 */

#ifndef RIEGL_RDB_POINTCLOUD_DATATYPES_HPP
#define RIEGL_RDB_POINTCLOUD_DATATYPES_HPP

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

#include <array>
#include <vector>
#include <limits>
#include <cstdlib>
#include <cstdint>
#include <type_traits>

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

namespace riegl {
namespace rdb {
namespace pointcloud {

//---< ENUM DataType >----------------------------------------------------------
/*!
 * \brief Point attribute access data type
 */
enum DataType
{
    NONE    =  0,     //!< undefined data type
    UINT8   =  1,     //!<  8 bit unsigned integer
    INT8    =  2,     //!<  8 bit   signed integer
    UINT16  =  3,     //!< 16 bit unsigned integer
    INT16   =  4,     //!< 16 bit   signed integer
    UINT32  =  5,     //!< 32 bit unsigned integer
    INT32   =  6,     //!< 32 bit   signed integer
    UINT64  =  7,     //!< 64 bit unsigned integer
    INT64   =  8,     //!< 64 bit   signed integer
    SINGLE  =  9,     //!< 32 bit single precision floating point value
    DOUBLE  = 10,     //!< 64 bit double precision floating point value
    FLOAT32 = SINGLE, //!< 32 bit single precision floating point value
    FLOAT64 = DOUBLE  //!< 64 bit double precision floating point value
};

//______________________________________________________________________________
/*!
 * \brief Get RDB data type enumerator from data type
 *
 * \see dataTypeOf()
 */
template <typename ValueType>
class DataTypeOf
{
private:
    template <bool Integer, bool Signed, std::size_t Size, typename dummy = void> struct data_type_of { /* void */ };
    template <typename dummy> struct data_type_of<true,  false, 1, dummy> { static const auto type = UINT8;   };
    template <typename dummy> struct data_type_of<true,  true,  1, dummy> { static const auto type = INT8;    };
    template <typename dummy> struct data_type_of<true,  false, 2, dummy> { static const auto type = UINT16;  };
    template <typename dummy> struct data_type_of<true,  true,  2, dummy> { static const auto type = INT16;   };
    template <typename dummy> struct data_type_of<true,  false, 4, dummy> { static const auto type = UINT32;  };
    template <typename dummy> struct data_type_of<true,  true,  4, dummy> { static const auto type = INT32;   };
    template <typename dummy> struct data_type_of<true,  false, 8, dummy> { static const auto type = UINT64;  };
    template <typename dummy> struct data_type_of<true,  true,  8, dummy> { static const auto type = INT64;   };
    template <typename dummy> struct data_type_of<false, true,  4, dummy> { static const auto type = FLOAT32; };
    template <typename dummy> struct data_type_of<false, true,  8, dummy> { static const auto type = FLOAT64; };

private:
    template <typename SomeType>
    struct remove_container // no container at all or not supported container
    {
        typedef SomeType type;
    };

    template <typename SomeType, std::size_t Size>
    struct remove_container< SomeType[Size] > // C style array
    {
        typedef SomeType type;
    };

    template <typename SomeType, std::size_t Size>
    struct remove_container< std::array<SomeType, Size> > // C++ array
    {
        typedef SomeType type;
    };

    template <typename SomeType, class Allocator>
    struct remove_container< std::vector<SomeType, Allocator> > // C++ vector
    {
        typedef SomeType type;
    };

private:
    typedef typename std::remove_cv     <ValueType>::type TempType1;
    typedef typename std::remove_pointer<TempType1>::type TempType2;
    typedef typename std::remove_cv     <TempType2>::type TempType3;
    typedef typename remove_container   <TempType3>::type TempType4;
    typedef typename remove_container   <TempType4>::type CoreType;

public:
    //! Result RDB data type enumerator for given ValueType
    static const DataType type = data_type_of<
        std::numeric_limits<CoreType>::is_integer,
        std::numeric_limits<CoreType>::is_signed,
        sizeof(CoreType)
    >::type;
};

//______________________________________________________________________________
/*!
 * \brief Convenience wrapper for DataTypeOf class
 *
 * Example:
 * \code
 *    std::cout << int(dataTypeOf< std::int32_t >());         // prints "6"
 *    std::cout << int(dataTypeOf< std::array<double,3> >()); // prints "10"
 * \endcode
 */
template <typename ValueType>
DataType dataTypeOf()
{
    return DataTypeOf<ValueType>::type;
}

//______________________________________________________________________________
/*!
 * \brief Convenience wrapper for DataTypeOf class
 *
 * Example:
 * \code
 *    float amplitude;
 *    std::array<double,3> xyz;
 *    std::cout << int(dataTypeOf(amplitude)); // prints "9"
 *    std::cout << int(dataTypeOf(xyz));       // prints "10"
 * \endcode
 */
template <typename ValueType>
DataType dataTypeOf(const ValueType& value)
{
    return DataTypeOf<ValueType>::type;
    (void)value; // unused
}

//______________________________________________________________________________
/*!
 * \brief Get pointer to variable or to data in a std::array or vector container
 */
template <typename ValueType>
ValueType* dataPointerOf(ValueType* const value)
{
    return value;
}
//! \copydoc dataPointerOf()
template <typename ValueType>
const ValueType* dataPointerOf(const ValueType* const value)
{
    return value;
}
//! \copydoc dataPointerOf()
template <typename ValueType>
ValueType* dataPointerOf(ValueType& value)
{
    return &value;
}
//! \copydoc dataPointerOf()
template <typename ValueType>
const ValueType* dataPointerOf(const ValueType& value)
{
    return &value;
}
//! \copydoc dataPointerOf()
template <typename ValueType, std::size_t Size>
ValueType* dataPointerOf(std::array<ValueType, Size>& value)
{
    return value.data();
}
//! \copydoc dataPointerOf()
template <typename ValueType, std::size_t Size>
const ValueType* dataPointerOf(const std::array<ValueType, Size>& value)
{
    return value.data();
}
//! \copydoc dataPointerOf()
template <typename ValueType, class Allocator>
ValueType* dataPointerOf(std::vector<ValueType, Allocator>& value)
{
    return value.data();
}
//! \copydoc dataPointerOf()
template <typename ValueType, class Allocator>
const ValueType* dataPointerOf(const std::vector<ValueType, Allocator>& value)
{
    return value.data();
}

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

#endif // RIEGL_RDB_POINTCLOUD_DATATYPES_HPP
