//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2019 California Institute of Technology
//
// LDASTools frameCPP is free software; you may redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 (GPLv2) of the
// License or at your discretion, any later version.
//
// LDASTools frameCPP is distributed in the hope that it will be useful, but
// without any warranty or even the implied warranty of merchantability
// or fitness for a particular purpose. See the GNU General Public
// License (GPLv2) for more details.
//
// Neither the names of the California Institute of Technology (Caltech),
// The Massachusetts Institute of Technology (M.I.T), The Laser
// Interferometer Gravitational-Wave Observatory (LIGO), nor the names
// of its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// You should have received a copy of the licensing terms for this
// software included in the file LICENSE located in the top-level
// directory of this package. If you did not, you can view a copy at
// http://dcc.ligo.org/M1500244/LICENSE
//

#ifndef FrameCPP_UNIVERSAL__IMPL__FrVectData_HH
#define FrameCPP_UNIVERSAL__IMPL__FrVectData_HH

#if !defined( SWIGIMPORTED )
#include <iostream>
#include <memory>
#include <vector>
#include <string>
#include <type_traits>
#include <stdexcept>

#include <boost/shared_array.hpp>
#include <boost/callable_traits.hpp>

#include "ldastoolsal/types.hh"

#include "framecpp/Common/Compression.hh"
#include "framecpp/Common/FrVect.hh"

#include "framecpp/Universal/impl/FrameSpec.hh"
#include "framecpp/Universal/impl/FrObjectMetaData.hh"
#include "framecpp/Universal/impl/DimensionData.hh"

#endif /* ! defined(SWIGIMPORTED)*/

#if !defined( SWIGIMPORTED )
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrVectImpl
        {
            class Data : public Impl::FrObjectMetaData< Data >,
                         public Common::FrVect
            {
            public:
                static constexpr INT_2U COMPRESSION_SCHEME_ENDIANNESS = 0x8000;

#if WORDS_BIGENDIAN
#define NATIVE_DECL( x ) x = BIGENDIAN_##x
#else
#define NATIVE_DECL( x ) x = LITTLEENDIAN_##x
#endif
#define CST_DECL( x, y )                                                       \
    BIGENDIAN_##x = y,                                                         \
    LITTLEENDIAN_##x = ( y | COMPRESSION_SCHEME_ENDIANNESS ), NATIVE_DECL( x )

                using dimension_type = DimensionImpl::Data;

                struct v3
                {
                    using name_type = std::string;
                    using compress_type = INT_2U;
                    using type_type = INT_2U;
                    using nData_type = INT_4U;
                    using nBytes_type = INT_4U;
                    using data_type = boost::shared_array< CHAR_U >;
                    using nDim_type = INT_4U;
                    using dimension_container_type =
                        std::vector< dimension_type >;
                    using unitY_type = std::string;

                    enum compression_scheme_type : INT_2U
                    {
                        //---------------------------------------------------------
                        // Declaration of compression modes
                        //---------------------------------------------------------
                        CST_DECL( RAW, 0x00 ),
                        CST_DECL( GZIP, 0x01 ),
                        CST_DECL( DIFF, 0x02 ),
                        CST_DECL( DIFF_GZIP, 0x03 ),
                    };

                public:
                    struct Tag
                    {
                    };
                };

                struct v4
                {
                    using name_type = v3::name_type;
                    using compress_type = v3::compress_type;
                    using type_type = v3::type_type;
                    using nData_type = v3::nData_type;
                    using nBytes_type = v3::nBytes_type;
                    using data_type = v3::data_type;
                    using nDim_type = v3::nDim_type;
                    using dimension_container_type =
                        v3::dimension_container_type;
                    using unitY_type = v3::unitY_type;

                    enum compression_scheme_type : INT_2U
                    {
                        //---------------------------------------------------------
                        // Declaration of compression modes
                        //---------------------------------------------------------
                        CST_DECL( RAW, 0x00 ),
                        CST_DECL( GZIP, 0x01 ),
                        CST_DECL( DIFF, 0x02 ),
                        CST_DECL( DIFF_GZIP, 0x03 ),
                        CST_DECL( ZERO_SUPPRESS_SHORT, 0x05 ),
                    };

                public:
                    struct Tag
                    {
                    };
                };

                struct v5
                {
                    using name_type = v4::name_type;
                    using compress_type = v4::compress_type;
                    using type_type = v4::type_type;
                    using nData_type = INT_8U;
                    using nBytes_type = INT_8U;
                    using data_type = v4::data_type;
                    using nDim_type = v4::nDim_type;
                    using dimension_container_type =
                        v4::dimension_container_type;
                    using unitY_type = v4::unitY_type;

                    enum compression_scheme_type : INT_2U
                    {
                        //---------------------------------------------------------
                        // Declaration of compression modes
                        //---------------------------------------------------------
                        CST_DECL( RAW, 0x00 ),
                        CST_DECL( GZIP, 0x01 ),
                        CST_DECL( DIFF, 0x02 ),
                        CST_DECL( DIFF_GZIP, 0x03 ),
                        CST_DECL( ZERO_SUPPRESS_SHORT, 0x05 ),
                        CST_DECL( ZERO_SUPPRESS_INT_FLOAT, 0x08 ),
                        //---------------------------------------------------------
                        // Declaration of meta compression modes
                        //---------------------------------------------------------
                        ZERO_SUPPRESS_SHORT_GZIP_OTHER = 6,
                        ZERO_SUPPRESS_OTHERWISE_GZIP = 10,

                        NONE = RAW
                    };

                public:
                    struct Tag
                    {
                    };
                };

                using v6 = v5;
                using v7 = v6;

                struct v8
                {
                    using name_type = v7::name_type;
                    using compress_type = v7::compress_type;
                    using type_type = v7::type_type;
                    using nData_type = v7::nData_type;
                    using nBytes_type = v7::nBytes_type;
                    using data_type = v7::data_type;
                    using nDim_type = v7::nDim_type;
                    using dimension_container_type =
                        v7::dimension_container_type;
                    using unitY_type = v7::unitY_type;

                    enum compression_scheme_type : INT_2U
                    {
                        //---------------------------------------------------------
                        // Declaration of compression modes
                        //---------------------------------------------------------
                        CST_DECL( RAW, 0x00 ),
                        CST_DECL( GZIP, 0x01 ),
                        CST_DECL( DIFF_GZIP, 0x03 ),
                        CST_DECL( ZERO_SUPPRESS_WORD_2, 0x05 ),
                        CST_DECL( ZERO_SUPPRESS_WORD_4, 0x08 ),
                        CST_DECL( ZERO_SUPPRESS_WORD_8, 0x0A ),
                        //---------------------------------------------------------
                        // Declaration of meta compression modes
                        //---------------------------------------------------------
                        ZERO_SUPPRESS_OTHERWISE_GZIP = 0x06,
                        BEST_COMPRESSION = 0x09,

                        //---------------------------------------------------------
                        // Backwards compatability
                        //---------------------------------------------------------
                        ZERO_SUPPRESS_SHORT = ZERO_SUPPRESS_WORD_2,
                        ZERO_SUPPRESS_INT_FLOAT = ZERO_SUPPRESS_WORD_4,

                        NONE = RAW
                    };

                public:
                    struct Tag
                    {
                    };
                };

                struct v9
                {
                    using compression_level_type = int;

                    enum compression_scheme_type : INT_2U
                    {
                        //---------------------------------------------------------
                        //---------------------------------------------------------
                        CST_DECL( RAW, 0x00 ),
                        CST_DECL( ZERO_SUPPRESS, 0x01 ),
                        CST_DECL( GZIP, 0x02 ),
                        CST_DECL( DIFF_GZIP, 0x04 ),
                        CST_DECL( ZSTD, 0x08 ),
                        CST_DECL( DIFF_ZSTD, 0x10 ),

                        //---------------------------------------------------------
                        // Declaration of meta modes
                        //---------------------------------------------------------
                        CST_DECL( ZERO_SUPPRESS_OTHERWISE_GZIP,
                                  ( 0x0FFF & ( ZERO_SUPPRESS | GZIP ) ) ),
                        CST_DECL( ZERO_SUPPRESS_OTHERWISE_ZSTD,
                                  ( 0x0FFF & ( ZERO_SUPPRESS | ZSTD ) ) ),
                        CST_DECL( BEST_COMPRESSION,
                                  ( 0x0FFF &
                                    ( ZERO_SUPPRESS | GZIP | DIFF_GZIP | ZSTD |
                                      DIFF_ZSTD ) ) ),
#undef GENERIC
                        NONE = RAW
                    };

#undef CST_DECL
#undef NATIVE_DECL

                    enum data_types_type
                    {
                        FR_VECT_C = 0,
                        FR_VECT_2S = 1,
                        FR_VECT_8R = 2,
                        FR_VECT_4R = 3,
                        FR_VECT_4S = 4,
                        FR_VECT_8S = 5,
                        FR_VECT_8C = 6,
                        FR_VECT_16C = 7,
                        FR_VECT_STRING = 8,
                        FR_VECT_2U = 9,
                        FR_VECT_4U = 10,
                        FR_VECT_8U = 11,
                        FR_VECT_1U = 12
                    };

                    using name_type = v8::name_type;
                    using compress_type = v8::compress_type;
                    using type_type = v8::type_type;
                    using nData_type = v8::nData_type;
                    using nBytes_type = v8::nBytes_type;
                    using data_type = v8::data_type;
                    using nDim_type = v8::nDim_type;
                    using dimension_container_type =
                        v8::dimension_container_type;
                    using unitY_type = v8::unitY_type;
                    using nDataValid_type = v8::nData_type;
                    using dataValidCompScheme_type = compression_scheme_type;
                    using nDataValidCompBytes_type = INT_8U;
                    using dataValid_type = boost::shared_array< CHAR_U >;

                public:
                    struct Tag
                    {
                    };
                };

                //-------------------------------------------------------

                using current_version = v9;

                using compression_scheme_type = current_version::compression_scheme_type;
                using compression_level_type = current_version::compression_level_type;
                using data_types_type = current_version::data_types_type;

                // static constexpr number_type A = number_type::A;
                static constexpr auto RAW = compression_scheme_type::RAW;
                static constexpr auto ZERO_SUPPRESS =
                    compression_scheme_type::ZERO_SUPPRESS;
                static constexpr auto GZIP = compression_scheme_type::GZIP;
                static constexpr auto DIFF_GZIP =
                    compression_scheme_type::DIFF_GZIP;
                static constexpr auto ZSTD = compression_scheme_type::ZSTD;
                static constexpr auto DIFF_ZSTD =
                    compression_scheme_type::DIFF_ZSTD;
                static constexpr auto ZERO_SUPPRESS_OTHERWISE_GZIP =
                    compression_scheme_type::ZERO_SUPPRESS_OTHERWISE_GZIP;
                static constexpr auto ZERO_SUPPRESS_OTHERWISE_ZSTD =
                    compression_scheme_type::ZERO_SUPPRESS_OTHERWISE_ZSTD;
                static constexpr auto BEST_COMPRESSION =
                    compression_scheme_type::BEST_COMPRESSION;

                static constexpr auto FR_VECT_C = data_types_type::FR_VECT_C;
                static constexpr auto FR_VECT_2S = data_types_type::FR_VECT_2S;
                static constexpr auto FR_VECT_8R = data_types_type::FR_VECT_8R;
                static constexpr auto FR_VECT_4R = data_types_type::FR_VECT_4R;
                static constexpr auto FR_VECT_4S = data_types_type::FR_VECT_4S;
                static constexpr auto FR_VECT_8S = data_types_type::FR_VECT_8S;
                static constexpr auto FR_VECT_8C = data_types_type::FR_VECT_8C;
                static constexpr auto FR_VECT_16C =
                    data_types_type::FR_VECT_16C;
                static constexpr auto FR_VECT_STRING =
                    data_types_type::FR_VECT_STRING;
                static constexpr auto FR_VECT_2U = data_types_type::FR_VECT_2U;
                static constexpr auto FR_VECT_4U = data_types_type::FR_VECT_4U;
                static constexpr auto FR_VECT_8U = data_types_type::FR_VECT_8U;
                static constexpr auto FR_VECT_1U = data_types_type::FR_VECT_1U;

                using name_type = current_version::name_type;
                using unitY_type = current_version::unitY_type;
                using compress_type = current_version::compress_type;
                using type_type = current_version::type_type;
                using nData_type = current_version::nData_type;
                using nBytes_type = current_version::nBytes_type;
                using nDim_type = current_version::nDim_type;
                using data_type = current_version::data_type;
                using dimension_container_type = current_version::dimension_container_type;
                using nDataValid_type = current_version::nDataValid_type;
                using dataValidCompScheme_type = current_version::dataValidCompScheme_type;
                using nDataValidCompBytes_type = current_version::nDataValidCompBytes_type;
                using dataValid_type = current_version::dataValid_type;

                using data_element_type = data_type::element_type;
                typedef data_element_type*       data_pointer_type;
                typedef data_element_type const* data_const_pointer_type;

                static const constexpr compression_level_type
                                       DEFAULT_GZIP_LEVEL = 6;

                virtual ~Data( )
                {
                }

                virtual Compression::compression_base_type
                Compression( ) const
                {
                    return ( 0x0FF & m_data.compress );
                }

                virtual void
                CompressData( INT_4U Scheme, INT_2U Level )
                {
                    Compress( compression_scheme_type( Scheme ), Level );
                }

                //-----------------------------------------------------------------
                /// \brief Allocate a block of memory suitable for storing data.
                ///
                /// \param[in] Type
                ///     The type of data that is to be stored in the buffer.
                /// \param[in] Dims
                ///     Information concearning each dimension of the array
                ///     of data to be stored in the buffer.
                /// \param[in] NDim
                ///     The number of dimensions of data to be stored in the
                ///     buffer
                ///
                /// \return
                ///     Upon successful allocation, an appropriated sized buffer
                ///     is returned.
                ///     An expection is thrown for all error conditions.
                //-----------------------------------------------------------------
                static data_type
                DataAlloc( type_type             Type,
                           const dimension_type* Dims,
                           const nDim_type       NDim = 1 )
                {
                    nData_type dsize = 1;

                    for ( nDim_type x = 0; x < NDim; ++x )
                    {
                        dsize *= Dims[ x ].GetNx( );
                    }
                    return DataAlloc( Type, dsize );
                }

                //-----------------------------------------------------------------
                /// \brief Allocate a block of memory suitable for storing data.
                ///
                /// \param[in] Type
                ///     The type of data that is to be stored in the buffer.
                /// \param[in] Dims
                ///     Information concearning each dimension of the array
                ///     of data to be stored in the buffer.
                /// \param[in] NDim
                ///     The number of dimensions of data to be stored in the
                ///     buffer
                ///
                /// \return
                ///     Upon successful allocation, an appropriated sized buffer
                ///     is returned.
                ///     An expection is thrown for all error conditions.
                //-----------------------------------------------------------------
                static data_type
                DataAlloc( type_type Type, nData_type NData )
                {
                    data_type retval;

                    switch ( Type )
                    {
                    case data_types_type::FR_VECT_C:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new CHAR[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_1U:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new CHAR_U[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_2S:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_2S[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_2U:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_2U[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_4S:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_4S[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_4U:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_4U[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_8S:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_8S[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_8U:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new INT_8U[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_4R:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new REAL_4[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_8R:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new REAL_8[ NData ] ) );
                        break;
                    case data_types_type::FR_VECT_8C:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new REAL_4[ NData * 2 ] ) );
                        break;
                    case data_types_type::FR_VECT_16C:
                        retval.reset( reinterpret_cast< data_pointer_type >(
                            new REAL_8[ NData * 2 ] ) );
                        break;
                        /*
                          ????
                          case data_types_type::FR_VECT_STR:
                          return sizeof( std::std::string );
                          ????
                        */
                    }

                    return retval;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the channel name.
                ///
                /// \return
                ///     The channel name
                //-----------------------------------------------------------------
                inline const name_type&
                GetName( ) const
                {
                    return m_data.name;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the compression algorithm number.
                ///
                /// \return
                ///     The compression algorithm number.
                //-----------------------------------------------------------------
                inline compress_type
                GetCompress( ) const
                {
                    return m_data.compress;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the vector class.
                ///
                /// \return
                ///     The vector class.
                //-----------------------------------------------------------------
                inline type_type
                GetType( ) const
                {
                    return m_data.type;
                }

                //-----------------------------------------------------------------
                //-----------------------------------------------------------------
                inline size_t
                GetTypeSize( ) const
                {
                    return GetTypeSize( m_data.type );
                }

                //-----------------------------------------------------------------
                /// \brief Get size of single data point of particular size.
                ///
                /// \param[in] Type
                ///     Data type ID.
                ///
                /// \return
                ///     Data size.
                //-----------------------------------------------------------------
                static size_t
                GetTypeSize( type_type Type )
                {
                    switch ( Type )
                    {
                    case data_types_type::FR_VECT_C:
                        return sizeof( CHAR );
                    case data_types_type::FR_VECT_2S:
                        return sizeof( INT_2S );
                    case data_types_type::FR_VECT_8R:
                        return sizeof( REAL_8 );
                    case data_types_type::FR_VECT_4R:
                        return sizeof( REAL_4 );
                    case data_types_type::FR_VECT_4S:
                        return sizeof( INT_4S );
                    case data_types_type::FR_VECT_8S:
                        return sizeof( INT_8S );
                    case data_types_type::FR_VECT_8C:
                        return ( sizeof( REAL_4 ) * 2 );
                    case data_types_type::FR_VECT_16C:
                        return ( sizeof( REAL_8 ) * 2 );
                        /*
                          ????
                          case data_types_type::FR_VECT_STR:
                          return sizeof( std::std::string );
                          ????
                        */
                    case data_types_type::FR_VECT_2U:
                        return sizeof( INT_2U );
                    case data_types_type::FR_VECT_4U:
                        return sizeof( INT_4U );
                    case data_types_type::FR_VECT_8U:
                        return sizeof( INT_8U );
                    case data_types_type::FR_VECT_1U:
                        return sizeof( CHAR_U );
                    }
                    // None of above: Perhaps add type for throw?
                    return 0;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the number of bytes in the compressed
                /// vector.
                ///
                /// \return
                ///     The number of bytes in the compressed vector.
                //-----------------------------------------------------------------
                inline nBytes_type
                GetNBytes( ) const
                {
                    return m_data.nBytes;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the number of sample elements in data
                /// series.
                ///
                /// \return
                ///     The number of sample elements in data series.
                //-----------------------------------------------------------------
                nData_type
                GetNData( ) const
                {
                    return m_data.nData;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the number of sample elements in data valid
                /// series.
                ///
                /// \return
                ///     The number of sample elements in data series.
                //-----------------------------------------------------------------
                inline nData_type
                GetNDataValid( ) const
                {
                    return m_data.nDataValid;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the number of bytes in the compressed
                /// dataValid vector.
                ///
                /// \return
                ///     The number of bytes in the compressed dataValid vector.
                //-----------------------------------------------------------------
                inline nBytes_type
                GetNDataValidBytes( ) const
                {
                    return m_data.nDataValidCompBytes;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the pointer to the data.
                ///
                /// Return a pointer to the data being managed by the FrVect
                /// structure.
                ///
                /// \return
                ///     The pointer to the data.
                ///
                /// \note
                ///     This will expand the data if it is currently compressed.
                ///
                /// \see GetDataRaw, GetDataUncompressed
                //-----------------------------------------------------------------
                inline data_type
                GetData( ) const
                {
                    return GetDataRaw( );
                }

                //-----------------------------------------------------------------
                /// @brief Retrieve the pointer to the data.
                ///
                /// Return a pointer to the data valid being managed by the
                /// FrVect structure.
                ///
                /// @return
                ///     The pointer to the data valid.
                ///
                /// @note
                ///     This will expand the data valid if it is currently
                ///     compressed.
                ///
                /// @see GetDataValidRaw
                //-----------------------------------------------------------------
                dataValid_type GetDataValid( );

                //-----------------------------------------------------------------
                /// @brief Retrieve the pointer to the data.
                ///
                /// Return a pointer to the data valid being managed by the
                /// FrVect structure.
                ///
                /// @return
                ///     The pointer to the data valid.
                ///
                /// @see GetDataValid
                //-----------------------------------------------------------------
                inline dataValid_type
                GetDataValidRaw( )
                {
                    return m_data.dataValid;
                }

                //-----------------------------------------------------------------
                /// @brief Retrieve the pointer to the data.
                ///
                /// Return a pointer to the data valid being managed by the
                /// FrVect structure.
                ///
                /// @return
                ///     The pointer to the data valid.
                ///
                /// @note
                ///     This will expand the data valid if it is currently
                ///     compressed.
                ///
                /// @see GetDataValidRaw
                //-----------------------------------------------------------------
                inline const CHAR_U*
                GetDataValidUncompressed( dataValid_type& Expanded )
                {
                    nDataValid_type expanded_size;

                    // Use the compression mapping for the version the data is ACTUALLY in
                    expandToBuffer( compress_type_map_for_version( m_data.actual_compression_version ),
                                    data_type_map( ),
                                    data_types_type::FR_VECT_1U,
                                    GetNDataValid( ),
                                    GetDataValidRaw( ).get( ),
                                    GetNDataValidBytes( ),
                                    GetDataValidCompress( ),
                                    ( ( GetDataValidCompress( ) &
                                        COMPRESSION_SCHEME_ENDIANNESS )
                                          ? BYTE_ORDER_LITTLE_ENDIAN
                                          : BYTE_ORDER_BIG_ENDIAN ),
                                    Expanded,
                                    expanded_size );
                    return Expanded.get( );
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the dataValid compression algorithm number.
                ///
                /// \return
                ///     The compression algorithm number.
                //-----------------------------------------------------------------
                inline compress_type
                GetDataValidCompress( ) const
                {
                    return m_data.dataValidCompScheme;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the pointer to the compressed data.
                ///
                /// \return
                ///     The pointer to the compressed data.
                //-----------------------------------------------------------------
                inline data_type
                GetDataRaw( ) const
                {
                    return m_data.data;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the pointer to the compressed data.
                ///
                /// \return
                ///     The pointer to the compressed data.
                //-----------------------------------------------------------------
                inline data_type
                GetDataRaw( )
                {
                    return m_data.data;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the pointer to the uncompressed data.
                ///
                /// \return
                ///     The pointer to the uncompressed data.
                //-----------------------------------------------------------------
                inline data_type
                GetDataUncompressed( )
                {
                    Uncompress( );

                    return m_data.data;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the pointer to the uncompressed data.
                ///
                /// \param[in] Expanded
                ///     Buffer into which the data is expanded.
                ///
                /// \return
                ///     The pointer to the uncompressed data.
                //-----------------------------------------------------------------
                inline const CHAR_U*
                GetDataUncompressed( data_type& Expanded ) const
                {
                    nBytes_type nBytes( 0 );

                    expandToBuffer( Expanded, nBytes );
                    if ( nBytes == 0 )
                    {
                        return m_data.data.get( );
                    }

                    return Expanded.get( );
                }

                template < class T >
                static type_type GetDataType( );

                //-----------------------------------------------------------------
                /// \brief Retrieve the number of dimensions
                ///
                /// \return
                ///     The number of dimensions
                //-----------------------------------------------------------------
                inline nDim_type
                GetNDim( ) const
                {
                    return m_data.dimension.size( );
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the Nth dimension
                ///
                /// \param[in] Offset
                ///     Index for desired dimension
                ///
                /// \return
                ///     The Nth dimension
                //-----------------------------------------------------------------
                inline const dimension_type&
                GetDim( nDim_type Offset ) const
                {
                    /// \todo
                    ///   Throw exception if value out of range
                    return m_data.dimension[ Offset ];
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the Nth dimension
                ///
                /// \param[in] Offset
                ///     Index for desired dimension
                ///
                /// \return
                ///     The Nth dimension
                //-----------------------------------------------------------------
                inline dimension_type&
                GetDim( nDim_type Offset )
                {
                    /// \todo
                    ///   Throw exception if value out of range
                    return m_data.dimension[ Offset ];
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve all the dimension information
                ///
                /// \return
                ///     The colletion of dimension data
                //-----------------------------------------------------------------
                inline dimension_container_type&
                GetDims( )
                {
                    return m_data.dimension;
                }

                //-----------------------------------------------------------------
                /// \brief Retrieve the description of how to interpret each
                /// element
                ///
                /// \return
                ///     The description of how to interpret each element
                //-----------------------------------------------------------------
                inline const unitY_type&
                GetUnitY( ) const
                {
                    return m_data.unitY;
                }

                inline void
                Compress( compression_scheme_type Scheme, int Level )
                {
                    if ( Scheme == GetCompress( ) )
                    {
                        // Nothing to do. Just return
                        return;
                    }

                    //---------------------------------------------------
                    // Uncompress the data first
                    //---------------------------------------------------
                    Uncompress( );

                    compress_block( GetType( ),
                                    Scheme,
                                    Level,
                                    m_data.nData,
                                    m_data.nBytes,
                                    m_data.data,
                                    m_data.compress );
                }

                //-------------------------------------------------------
                /// \brief Expand compressed data.
                //-------------------------------------------------------
                void Uncompress( );

                //-------------------------------------------------------
                /// \brief Set the current FrVect with the MIME data
                //-------------------------------------------------------
                void
                MimeData( const std::string& MimeType,
                          void*              Data,
                          nBytes_type        DataSize )
                {
                    //---------------------------------------------------------------------
                    // Setup dimension information
                    //---------------------------------------------------------------------
                    m_data.type = data_types_type::FR_VECT_1U;
                    SetUnitY( MIME_TYPE_KEYWORD );
                    GetDims( ).erase( GetDims( ).begin( ), GetDims( ).end( ) );
                    GetDims( ).push_back( dimension_type(
                        DataSize, MIME_TYPE_DX, MimeType, MIME_TYPE_START_X ) );
                    //---------------------------------------------------------------------
                    // Fill data field with the blob
                    //---------------------------------------------------------------------
                    data_copy(
                        reinterpret_cast< data_const_pointer_type >( Data ),
                        DataSize );
                    SetNData( DataSize );
                }

                //-----------------------------------------------------------------
                /// \brief Establish the channel name.
                ///
                /// Assign a new value for the channel name.
                ///
                /// \param[in] Name
                ///     The channel name
                //-----------------------------------------------------------------
                void
                SetName( const name_type& Name )
                {
                    m_data.name = Name;
                }

                //-----------------------------------------------------------------
                /// \brief Set the description of how to interpret each element
                ///
                /// Assign a new value for the channel name.
                ///
                /// \param[in] UnitY
                ///     New description
                ///
                /// \return
                ///     The description of how to interpret each element
                //-----------------------------------------------------------------
                void
                SetUnitY( const unitY_type& UnitY )
                {
                    m_data.unitY = UnitY;
                }

                //-----------------------------------------------------------------
                //-----------------------------------------------------------------
                void
                SetNData( nData_type NData )
                {
                    m_data.nData = NData;
                }

                //-----------------------------------------------------------------
                /// \brief Store data valid information
                ///
                /// \param[in]
                //-----------------------------------------------------------------
                void SetDataValid( const CHAR_U*            DataValid,
                                   nDataValid_type          NDataValid,
                                   dataValidCompScheme_type Compression =
                                       compression_scheme_type::RAW );

                //-----------------------------------------------------------------
                //-----------------------------------------------------------------
                template < typename SOURCE_COMPRESSION_TYPE,
                           typename DESTINATION_COMPRESSION_TYPE >
                void
                TranslateCompressMode(
                    SOURCE_COMPRESSION_TYPE       SourceCompressionMode,
                    DESTINATION_COMPRESSION_TYPE& DestinationCompressionMode )
                {
                    int source_version =
                        getVersionNumber( SourceCompressionMode );
                    int destination_version =
                        getVersionNumber( DestinationCompressionMode );
                    if ( source_version == destination_version )
                    {
                        DestinationCompressionMode = SourceCompressionMode;
                    }
                    else if ( source_version < destination_version )
                    {
                        // Up convert
                    }
                    else
                    {
                        // Down convert
                    }
                }

                //---------------------------------------------------------------
                /// \brief equality operator
                ///
                /// \param[in] RHS
                ///     The fr_history_data_type object to be compared.
                ///
                /// \return
                ///     The value true is returned if this object is
                ///     equivelent to the RHS instance; false otherwise.
                //---------------------------------------------------------------
                bool
                operator==( const Data& RHS ) const
                {
#define CMP__( X ) ( X == RHS.X )
                    return ( ( &RHS == this ) || //
                             ( CMP__( m_data ) ) );
#undef CMP__
                }

            protected:
                enum
                {
                    MODE_RAW = compression_scheme_type::BIGENDIAN_RAW,
                    MODE_GZIP = compression_scheme_type::BIGENDIAN_GZIP,
                    MODE_DIFF_GZIP =
                        compression_scheme_type::BIGENDIAN_DIFF_GZIP,
                    MODE_ZERO_SUPPRESS =
                        compression_scheme_type::BIGENDIAN_ZERO_SUPPRESS,
                    MODE_ZSTD = compression_scheme_type::BIGENDIAN_ZSTD,
                    MODE_DIFF_ZSTD =
                        compression_scheme_type::BIGENDIAN_DIFF_ZSTD
                };

                enum
                {
                    MODE_BIGENDIAN = compression_scheme_type::BIGENDIAN_RAW,
                    MODE_LITTLEENDIAN =
                        compression_scheme_type::LITTLEENDIAN_RAW,
                    MODE_HOSTENDIAN = compression_scheme_type::RAW
                };

                /**
                 * @brief Helper template to always trigger a static assert for
                 * unsupported types.
                 *
                 * This template is used in conjunction with `static_assert` to
                 * ensure that unsupported types are caught at compile time.
                 *
                 * @tparam T The type that is not supported.
                 */
                template < typename T >
                static constexpr bool always_false_v = false;

                void compressToBuffer(
                    const Compression::compress_type_mapping&
                        CompressionMapping,
                    const Compression::compress_type_reverse_mapping&
                                                          CompressionReverseMapping,
                    const Compression::data_type_mapping& DataTypeMapping,
                    const INT_4U                          DataType,
                    const INT_8U                          NData,
                    const CHAR_U*                         Source,
                    const INT_8U                          SourceNBytes,
                    const INT_4U                          SourceCompressionMode,
                    Compression::output_type&             Dest,
                    INT_8U&                               DestNBytes,
                    INT_4U&                               DestCompressionMode,
                    const INT_4U                          Level ) const;

                void expandToBuffer(
                    const Compression::compress_type_mapping&
                                                          CompressionMapping,
                    const Compression::data_type_mapping& DataTypeMapping,
                    const INT_4U                          DataType,
                    const INT_8U                          NData,
                    const CHAR_U*                         Source,
                    const INT_8U                          SourceNBytes,
                    const INT_4U                          SourceCompressionMode,
                    const INT_4U                          SourceByteOrder,
                    Compression::output_type&             Dest,
                    INT_8U&                               DestNBytes ) const;

                /**
                 * @brief Retrieves the version number for a given type.
                 *
                 * This function uses template conditionals to check the type of
                 * the input and return the appropriate version number based on
                 * the associated tag structure.
                 *
                 * @tparam T The type whose version number is to be retrieved.
                 * @param value The instance of the type (unused in this
                 * function).
                 * @return The version number corresponding to the type's
                 * namespace.
                 * @throws static_assert if the type is not supported.
                 */
                template < typename T >
                int getVersionNumber( T )
                {
                    if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v3::Tag > )
                        {
                            return 3;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v4::Tag > )
                        {
                            return 4;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v5::Tag > )
                        {
                            return 5;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v6::Tag > )
                        {
                            return 6;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v7::Tag > )
                        {
                            return 7;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v8::Tag > )
                        {
                            return 8;
                        }
                    else if
                        constexpr(
                            std::is_same_v<
                                typename std::remove_cv_t< decltype( T::Tag ) >,
                                v9::Tag > )
                        {
                            return 9;
                        }
                    else
                    {
                        static_assert( always_false_v< T >,
                                       "Unsupported version type" );
                    }
                }

                //-------------------------------------------------------
                /// @brief Compress a block data
                ///
                /// @param[in] Scheme
                ///   Desired compression method
                /// @param[in,out] NData
                ///   Block of data to be compressed.
                ///   The compressed buffer will be stored in this
                ///   variable.
                /// @para[in,out] NBytes
                ///   On input, the number of bytes in NData.
                ///   On output, the number of bytes in the compressed
                ///   NData buffer.
                /// @param[in,out] Block,
                ///   Block of data to be compressed.
                ///   Upon output, the compressed form of the block
                /// @param[out] CompressionScheme
                ///   The compression scheme
                //-------------------------------------------------------
                template < typename NDATA_T,
                           typename NBYTES_T,
                           typename DATA_T >
                void
                compress_block( type_type                BlockType,
                                compression_scheme_type  Scheme,
                                int                      Level,
                                NDATA_T&                 NData,
                                NBYTES_T&                NBytes,
                                DATA_T&                  Block,
                                compression_scheme_type& CompressionScheme )
                {
                    //---------------------------------------------------------------------
                    // Compress the data
                    //---------------------------------------------------------------------

                    compress_type compress =
                        ( static_cast< compress_type >( Scheme ) & 0xFF );
                    compress_type retry_compress = 0;
                    const INT_8U  nData( NData );
                    const INT_8U  nBytes( NBytes );
                    data_type     data_out( nullptr );
                    nData_type    data_out_len( 0 );

                    if ( ( compress &
                           compression_scheme_type::ZERO_SUPPRESS ) &&
                         ( ( BlockType != data_types_type::FR_VECT_C ) &&
                           ( BlockType != data_types_type::FR_VECT_1U ) &&
                           ( BlockType != data_types_type::FR_VECT_STRING ) ) )
                    {
                        //-----------------------------------------------
                        // Determine the retry scheme, if any
                        //-----------------------------------------------
                        if ( compress & compression_scheme_type::GZIP )
                        {
                            retry_compress =
                                ( compression_scheme_type::GZIP & 0xFF );
                        }
                        else if ( compress & compression_scheme_type::ZSTD )
                        {
                            retry_compress =
                                ( compression_scheme_type::ZSTD & 0xFF );
                        }
                        //-----------------------------------------------
                        // compress is set here since the origional
                        //   content is needed to determine the retry
                        //   scheme
                        //-----------------------------------------------
                        compress =
                            ( compression_scheme_type::ZERO_SUPPRESS & 0xFF );
                    }
                    else if ( ( compress &
                                compression_scheme_type::DIFF_GZIP ) &&
                              !( ( BlockType == data_types_type::FR_VECT_C ) ||
                                 ( BlockType == data_types_type::FR_VECT_1U ) ||
                                 ( BlockType == data_types_type::FR_VECT_2S ) ||
                                 ( BlockType == data_types_type::FR_VECT_2U ) ||
                                 ( BlockType == data_types_type::FR_VECT_4S ) ||
                                 ( BlockType ==
                                   data_types_type::FR_VECT_4U ) ) )
                    {
                        compress = ( compression_scheme_type::GZIP & 0xFF );
                    }
                    else if ( compress ==
                              ( compression_scheme_type::
                                    ZERO_SUPPRESS_OTHERWISE_GZIP &
                                0xFF ) )
                    {
                        compress = ( compression_scheme_type::GZIP & 0xFF );
                    }
                    else if ( compress ==
                              ( compression_scheme_type::
                                    ZERO_SUPPRESS_OTHERWISE_ZSTD &
                                0xFF ) )
                    {
                        compress = ( compression_scheme_type::ZSTD & 0xFF );
                    }
                compression_retry:
                {
                    using compress_mode_type = INT_4U;
                    auto compress_mode =
                        static_cast< compress_mode_type >( compress );

                    FrameCPP::Compression::Compress(
                        compress_mode,
                        Level,
                        compress_type_map( ),
                        compress_type_reverse_map( ),
                        BlockType,
                        data_type_map( ),
                        Block.get( ),
                        nData,
                        nBytes,
                        data_out,
                        data_out_len );

                    if ( data_out && data_out_len )
                    {
                        //-------------------------------------------------------------------
                        // Save the compressed data
                        //-------------------------------------------------------------------
                        NBytes = nBytes_type( data_out_len );
                        Block = data_out;
                        //-------------------------------------------------------------------
                        // Save the compression mode
                        //-------------------------------------------------------------------
                        CompressionScheme =
                            static_cast< std::remove_reference_t< decltype(
                                CompressionScheme ) > >( MODE_HOSTENDIAN |
                                                         ( compress & 0xFF ) );
                    }
                    else
                    {
                        if ( retry_compress )
                        {
                            compress = retry_compress;
                            retry_compress = 0x00;
                            goto compression_retry;
                        }
                    }
                }
                }

                template < typename DATA_T, typename NBYTES_T >
                inline void
                uncompress_block( compression_scheme_type& CompressionScheme,
                                  NBYTES_T&                NBytes,
                                  DATA_T&                  Block )
                {
                    //---------------------------------------------------------------------
                    // Check if there is anything that needs to be done
                    //---------------------------------------------------------------------
                    if ( CompressionScheme == compression_scheme_type::RAW )
                    {
                        // Nothing to do. The buffer is already uncompressed
                        return;
                    }

                    //---------------------------------------------------------------------
                    // There is something to be done
                    //---------------------------------------------------------------------
                    nBytes_type nBytes( NBytes );
                    data_type   expanded_buffer;

                    expandToBuffer( expanded_buffer, nBytes );

                    //---------------------------------------------------------------------
                    // Now record what has been done
                    //---------------------------------------------------------------------
                    if ( expanded_buffer.get( ) )
                    {
                        /* Transfer ownership */
                        Block = expanded_buffer;
                        NBytes = NBYTES_T( nBytes );
                        CompressionScheme = compression_scheme_type::RAW;
                    }
                }

                // Compression type mapping functions
                // Moved to FrVect.cc to avoid ODR violations from function-local statics
                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map( );

                static FrameCPP::Compression::compress_type_reverse_mapping&
                compress_type_reverse_map( );

                //=================================================================
                // Version-Specific Compression Mappings
                //=================================================================

                // Version-specific compression mapping functions
                // Moved to FrVect.cc to avoid ODR violations from function-local statics
                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map_v8( );

                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map_v5( );

                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map_v4( );

                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map_v3( );

                /**
                 * @brief Select the appropriate compression type map for a frame spec version
                 *
                 * @param version Frame specification version number (3-9)
                 * @return Reference to the appropriate compression type mapping
                 * @throws std::invalid_argument if version is not supported
                 */
                static FrameCPP::Compression::compress_type_mapping&
                compress_type_map_for_version( INT_2U version )
                {
                    switch ( version )
                    {
                        case 9:
                            return compress_type_map( );  // V9 (current)
                        case 8:
                        case 7:
                        case 6:
                            return compress_type_map_v8( );
                        case 5:
                            return compress_type_map_v5( );
                        case 4:
                            return compress_type_map_v4( );
                        case 3:
                            return compress_type_map_v3( );
                        default:
                        {
                            std::ostringstream msg;
                            msg << "Unsupported frame spec version for compression: "
                                << version;
                            throw std::invalid_argument( msg.str( ) );
                        }
                    }
                }

                inline void
                data_alloc( nBytes_type NBytes )
                {
                    m_data.data.reset( new CHAR_U[ NBytes ] );
                    m_data.nBytes = NBytes;
                }

                inline void
                data_copy( data_const_pointer_type Data, nBytes_type NBytes )
                {
                    // Invalidate conversion cache when data changes
                    m_data.m_conversion_cache.invalidate();

                    if ( ( NBytes == 0 ) ||
                         ( Data == (const data_pointer_type)NULL ) )
                    {
                        m_data.data.reset( );
                    }
                    else
                    {
                        if ( ( m_data.nBytes != NBytes ) || ( !m_data.data ) )
                        {
                            m_data.data.reset( new CHAR_U[ NBytes ] );
                            m_data.nBytes = NBytes;
                        }
                        std::copy( Data, Data + NBytes, m_data.data.get( ) );
                    }
                }

                // Data type mapping function
                // Moved to FrVect.cc to avoid ODR violations from function-local statics
                static FrameCPP::Compression::data_type_mapping&
                data_type_map( );

                inline void
                expandToBuffer( data_type& Dest, nBytes_type& DestNBytes ) const
                {
                    // Use the compression mapping for the version the data is ACTUALLY in
                    expandToBuffer(
                        compress_type_map_for_version( m_data.actual_compression_version ),
                        data_type_map( ),
                        GetType( ),
                        GetNData( ),
                        GetDataRaw( ).get( ),
                        GetNBytes( ),
                        GetCompress( ),
                        ( ( GetCompress( ) & COMPRESSION_SCHEME_ENDIANNESS )
                              ? BYTE_ORDER_LITTLE_ENDIAN
                              : BYTE_ORDER_BIG_ENDIAN ),
                        Dest,
                        DestNBytes );
                }

                //---------------------------------------------------------------
                /// \brief Cache for lazy conversion to avoid double-conversion
                ///
                /// This cache stores the result of compressDemote() conversion
                /// to ensure pBytes() and pWrite() use identical values.
                /// The cache is invalidated whenever the FrVect data is modified.
                //---------------------------------------------------------------
                struct ConversionCache
                {
                    INT_2U target_version = 0;              ///< Frame version converted to
                    INT_2U compression_mode = 0;            ///< Converted compression mode
                    boost::shared_array<CHAR_U> converted_data;  ///< Converted data buffer
                    nBytes_type converted_nbytes = 0;       ///< Size of converted data
                    bool valid = false;                     ///< Cache validity flag

                    /// Invalidate the cache
                    void invalidate() { valid = false; }
                };

                struct data_definition_type
                {
                public:
                    //---------------------------------------------------------------
                    /// Channel name.
                    ///
                    /// \note
                    ///     Not required to be unique.
                    //---------------------------------------------------------------
                    name_type name;
                    //---------------------------------------------------------------
                    /// Compression algorithm number
                    //---------------------------------------------------------------
                    compression_scheme_type compress;
                    //---------------------------------------------------------------
                    /// Frame spec version of the actual compression format
                    ///
                    /// Tracks which frame spec version's compression algorithm
                    /// the data is actually using. This is needed because during
                    /// version conversion, the compress field may be changed without
                    /// recompressing the data (when formats are compatible).
                    ///
                    /// When compress field is changed without recompression,
                    /// this field stays unchanged to track the actual data format.
                    /// When data is recompressed, this field is updated to match
                    /// the target version.
                    ///
                    /// Default: 9 (current version)
                    //---------------------------------------------------------------
                    INT_2U actual_compression_version;
                    //---------------------------------------------------------------
                    /// Vector class
                    //---------------------------------------------------------------
                    type_type type;
                    //---------------------------------------------------------------
                    /// Number of sample elements in data series.
                    //---------------------------------------------------------------
                    nData_type nData;
                    //---------------------------------------------------------------
                    /// Number of bytes in the compressed vector.
                    //---------------------------------------------------------------
                    nBytes_type nBytes;
                    //---------------------------------------------------------------
                    /// Compressed data series.
                    //---------------------------------------------------------------
                    data_type data;
                    //---------------------------------------------------------------
                    /// dimension_type data for data series.
                    //---------------------------------------------------------------
                    dimension_container_type dimension;
                    //---------------------------------------------------------------
                    /// String describing how to interpret the value of each
                    /// element.
                    //---------------------------------------------------------------
                    unitY_type unitY;
                    //---------------------------------------------------------------
                    /// Number of bytes in the data valid vector.
                    //---------------------------------------------------------------
                    nDataValid_type nDataValid = 0;
                    //---------------------------------------------------------------
                    /// Compression scheme used by dataValid vector
                    //---------------------------------------------------------------
                    dataValidCompScheme_type dataValidCompScheme =
                        compression_scheme_type::RAW;
                    //---------------------------------------------------------------
                    /// Number of bytes in the compressed data valid vector.
                    //---------------------------------------------------------------
                    nDataValidCompBytes_type nDataValidCompBytes = 0;
                    //---------------------------------------------------------------
                    /// This is an array of dataValid value covering the span of
                    /// the FrVect.data. A value of 0 in teh array signifies the
                    /// corresponding block of data is valid. A non-zero value
                    /// in the array signifies data as suspect
                    //---------------------------------------------------------------
                    dataValid_type dataValid;
                    //---------------------------------------------------------------
                    /// Mutable cache for version conversion to avoid double-conversion
                    /// between pBytes() and pWrite() calls
                    //---------------------------------------------------------------
                    mutable ConversionCache m_conversion_cache;

                    data_definition_type( )
                        : name( "" ), compress( compression_scheme_type::RAW ),
                          actual_compression_version( 9 ),
                          type( data_types_type::FR_VECT_C ), nData( 0 ),
                          nBytes( 0 ), dimension( ), unitY( "" )
                    {
                    }

                    data_definition_type( const std::string&    n,
                                          INT_2U                t,
                                          const byte_order_type byte_order,
                                          nDim_type             ndim,
                                          const dimension_type* dims,
                                          const std::string&    unit )
                    {
                        set( n, t, byte_order, ndim, dims, unit );
                    }

                    void
                    copy_core( const data_definition_type& Source )
                    {
                        // Invalidate conversion cache when copying
                        m_conversion_cache.invalidate();

                        name = Source.name;
                        compress = Source.compress;
                        actual_compression_version = Source.actual_compression_version;
                        type = Source.type;
                        nData = Source.nData;
                        nBytes = Source.nBytes;

                        for ( std::vector< dimension_type >::const_iterator
                                  d( Source.dimension.begin( ) ),
                              d_end( Source.dimension.end( ) );
                              d != d_end;
                              ++d )
                        {
                            dimension.push_back( *d );
                        }

                        unitY = Source.unitY;

                        nDataValid = Source.nDataValid;
                        dataValidCompScheme = Source.dataValidCompScheme;
                        nDataValidCompBytes = Source.nDataValidCompBytes;
                        dataValid.reset( new CHAR_U[ nDataValidCompBytes ] );
                        std::copy( Source.dataValid.get( ),
                                   Source.dataValid.get( ) +
                                       nDataValidCompBytes,
                                   dataValid.get( ) );
                    }

                    static FrameCPP::cmn_streamsize_type
                    calc_nData(
                        const std::vector< dimension_type >& Dimensions )
                    {
                        FrameCPP::cmn_streamsize_type ret( 0 );

                        if ( Dimensions.size( ) > 0 )
                        {
                            ret = 1;
                            for ( std::vector< dimension_type >::const_iterator
                                      i( Dimensions.begin( ) );
                                  i != Dimensions.end( );
                                  i++ )
                            {
                                ret *= ( *i ).GetNx( );
                            }
                        }
                        return ret;
                    }

                    void
                    set( const std::string&    n,
                         INT_2U                t,
                         const byte_order_type byte_order,
                         nDim_type             ndim,
                         const dimension_type* dims,
                         const std::string&    unit )
                    {
                        // Validate input parameters
                        if ( ndim == 0 )
                        {
                            throw std::invalid_argument(
                                "FrVect constructor: nDim must be greater than 0. "
                                "FrVect represents n-dimensional vector data and cannot be dimensionless." );
                        }
                        if ( ndim > 0 && dims == nullptr )
                        {
                            throw std::invalid_argument(
                                "FrVect constructor: dims cannot be nullptr when nDim > 0. "
                                "An n-dimensional vector requires dimension information." );
                        }

                        name = n;
                        type = t;
                        unitY = unit;
                        compress = compression_scheme_type::RAW;
                        actual_compression_version = 9;  // Default to current version

                        for ( nDim_type x = 0; x < ndim; ++x, ++dims )
                        {
                            dimension.push_back( *dims );
                        }

                        nData = calc_nData( dimension );
                        nBytes = GetTypeSize( type ) * nData;
                    }

                    bool
                    operator==( const data_definition_type& RHS ) const
                    {
#if LM_INFO
                        if ( name != RHS.name )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( compress != RHS.compress )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( type != RHS.type )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( nData != RHS.nData )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( nBytes != RHS.nBytes )
                        {
                            std::cerr << "DEBUG: " << __LINE__
                                      << " nBytes: " << nBytes
                                      << " RHS.nBytes: " << RHS.nBytes
                                      << std::endl;
                        }
                        if ( dimension != RHS.dimension )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( unitY != RHS.unitY )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        if ( ( nBytes == RHS.nBytes ) &&
                             ( std::equal( data.get( ),
                                           data.get( ) + nBytes,
                                           RHS.data.get( ) ) == false ) )
                        {
                            std::cerr << "DEBUG: " << __LINE__ << std::endl;
                        }
                        std::cerr << "DEBUG: nData: " << nData << std::endl;
#endif
                        return ( ( this == &RHS ) ||
                                 ( ( name == RHS.name ) &&
                                   ( compress == RHS.compress ) &&
                                   ( type == RHS.type ) &&
                                   ( nData == RHS.nData ) &&
                                   ( nBytes == RHS.nBytes ) &&
                                   ( dimension == RHS.dimension ) &&
                                   ( unitY == RHS.unitY ) &&
                                   ( ( data == RHS.data ) ||
                                     ( std::equal( data.get( ),
                                                   data.get( ) + nBytes,
                                                   RHS.data.get( ) ) ) ) &&
                                   ( nDataValid == RHS.nDataValid ) &&
                                   ( dataValidCompScheme ==
                                     RHS.dataValidCompScheme ) &&
                                   ( nDataValidCompBytes ==
                                     RHS.nDataValidCompBytes ) &&
                                   ( ( dataValid == RHS.dataValid ) ||
                                     ( std::equal(
                                         dataValid.get( ),
                                         dataValid.get( ) + nDataValidCompBytes,
                                         RHS.dataValid.get( ) ) ) ) ) );
                    }
                };

                data_definition_type m_data;

            private:
                static const constexpr char* MIME_TYPE_KEYWORD = "MIMETYPE";
                static const constexpr dimension_type::dx_type MIME_TYPE_DX = 1;
                static const constexpr dimension_type::startX_type
                                       MIME_TYPE_START_X = 0;
            }; // namespace FrVectImpl

            //-----------------------------------------------------------
            /// Return a pointer to the data valid being managed by the
            /// FrVect structure.
            //-----------------------------------------------------------
            inline Data::dataValid_type
            Data::GetDataValid( )
            {
                uncompress_block( m_data.dataValidCompScheme,
                                  m_data.nDataValidCompBytes,
                                  m_data.dataValid );
                return m_data.dataValid;
            }

            //-----------------------------------------------------------
            //-----------------------------------------------------------
            inline void
            Data::SetDataValid( const CHAR_U*            DataValid,
                                nDataValid_type          NDataValid,
                                dataValidCompScheme_type Compression )
            {
                m_data.nDataValid = NDataValid;
                m_data.nDataValidCompBytes = NDataValid;
                m_data.dataValidCompScheme = compression_scheme_type::RAW;
                if ( m_data.nDataValid == 0 )
                {
                    m_data.dataValid.reset( );
                    return;
                }
                if ( Compression == compression_scheme_type::RAW )
                {
                    m_data.dataValid.reset(
                        new CHAR_U[ m_data.nDataValidCompBytes ] );
                    std::copy( DataValid,
                               DataValid + m_data.nDataValidCompBytes,
                               m_data.dataValid.get( ) );
                }
                else
                {
                    compress_block( data_types_type::FR_VECT_4U,
                                    Compression,
                                    DEFAULT_GZIP_LEVEL,
                                    m_data.nDataValid,
                                    m_data.nDataValidCompBytes,
                                    m_data.dataValid,
                                    m_data.dataValidCompScheme );
                }
            }

            //-----------------------------------------------------------
            /// In general, uncompressing follows three steps:
            ///
            /// 1) Gunzip the data.
            /// 2) Fix byte-ordering.
            /// 3) Integrate.
            ///
            /// Depending upon the compression type and byte-ordering
            /// differences, not all of these steps are needed.
            ///
            /// After decompression, the data is in uncompressed (RAW) format
            /// in the current version (v9), so we update actual_compression_version.
            //-----------------------------------------------------------
            inline void
            Data::Uncompress( )
            {
                uncompress_block( m_data.compress, m_data.nBytes, m_data.data );

                // After decompression, data is now in current (v9) uncompressed format
                m_data.actual_compression_version = 9;
            }

        } // namespace FrVectImpl
    } // namespace Universal
} // namespace FrameCPP

FR_OBJECT_META_DATA_DECLARE_UNIVERSAL( FrVectImpl )

#endif /* ! defined(SWIGIMPORTED) */

#endif /* FrameCPP_UNIVERSAL__IMPL__FrVectData_HH */
