//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2018 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_Dimension_HH
#define FrameCPP_UNIVERSAL__IMPL_Dimension_HH

// System Includes
#if !defined( SWIGIMPORTED )
#include <memory>
#include <string>

#include "ldastoolsal/types.hh"

#include "framecpp/Universal/impl/FrameSpec.hh"
#include "framecpp/Universal/impl/STRING.hh"
#endif /* ! defined(SWIGIMPORTED) */

namespace FrameCPP
{

    namespace Universal
    {

        namespace DimensionImpl
        {

            //------------------------------------------------------------------
            ///
            /// \brief Dimension information for a Vect.
            ///
            /// This is a simple class storing dimension information for a Vect
            /// object. It stores the following: <p><ol>
            ///     <li> The dimension length. </li>
            ///     <li> Scale factor </li>
            ///     <li> The units (unit per step size along the coordintate).
            ///     </li>
            /// </ol>
            ///
            class Data
            {
            public:
                struct v3
                {
                    using nDim_type = INT_4U;
                    using nx_type = INT_4U;
                    using dx_type = REAL_8;
                    using unitX_type = std::string;

                    using io_unitX_type = STRING;
                };

                struct v4
                {
                    using nDim_type = v3::nDim_type;
                    using nx_type = v3::nx_type;
                    using dx_type = v3::dx_type;
                    using startX_type = REAL_8;
                    using unitX_type = v3::unitX_type;

                    using io_unitX_type = v3::io_unitX_type;
                };

                struct v5
                {
                    using nDim_type = v4::nDim_type;
                    using nx_type = INT_8U;
                    using dx_type = v4::dx_type;
                    using startX_type = v4::startX_type;
                    using unitX_type = v4::unitX_type;

                    using io_unitX_type = v4::io_unitX_type;
                };

                using v6 = v5;
                using v7 = v6;
                using v8 = v7;
                using v9 = v8;

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

                using current_version = v9;

                using nDim_type = current_version::nDim_type;
                using nx_type = current_version::nx_type;
                using dx_type = current_version::dx_type;
                using startX_type = current_version::startX_type;
                using unitX_type = current_version::unitX_type;

                using io_unitX_type = current_version::io_unitX_type;

                /* Constructors/Destructor */
                Data( ) = default;

                Data( const Data& dim ) = default;
                ;

                explicit Data( nx_type            nx,
                               dx_type            dx = 1.0,
                               const std::string& unitX = "",
                               startX_type        startX = 0.0 )
                    : mNx( nx ), //
                      mDx( dx ), //
                      mUnitX( unitX ), //
                      mStartX( startX )
                {
                }

                /* Operator Overloads */
#if !defined( SWIG )
                //-----------------------------------------------------------------
                ///
                /// \brief Assignment operator.
                ///
                /// \param[in] dim
                ///     The object to assign from.
                ///
                /// \return
                ///     This object.
                ///
                const Data& operator=( const Data& dim );
#endif /* ! defined(SWIG) */

                //----------------------------------------------------------------
                ///
                /// \brief Equal comparison.
                ///
                /// \param[in] dim
                ///     The object to compare with.
                ///
                /// \return
                ///     true if the objects are equal, false otherwise.
                ///
                bool operator==( const Data& dim ) const;

                //----------------------------------------------------------------
                ///
                /// \brief Not equal comparison.
                ///
                /// \param[in] dim
                ///     The object to compare with.
                ///
                /// \return
                ///     false if the objects are equal, true otherwise.
                ///
                bool operator!=( const Data& dim ) const;

                /* Accessors */
                //----------------------------------------------------------------
                ///
                /// \brief Get the dimension length.
                ///
                /// \return
                ///     The dimension length.
                ///
                //----------------------------------------------------------------
                nx_type GetNx( ) const;

                //----------------------------------------------------------------
                ///
                /// \brief Get the scale factor.
                ///
                /// \return
                ///     The scale factor.
                ///
                //----------------------------------------------------------------
                dx_type GetDx( ) const;

                //----------------------------------------------------------------
                ///
                /// \brief Get data set origin.
                ///
                /// \return
                ///     Data set origin.
                ///
                //----------------------------------------------------------------
                startX_type GetStartX( ) const;

                //----------------------------------------------------------------
                ///
                /// \brief Get the units.
                ///
                /// \return
                ///     The units.
                ///
                //----------------------------------------------------------------
                const unitX_type& GetUnitX( ) const;

                //-----------------------------------------------------------------
                /// \brief Set sample spacing
                ///
                /// \param[in] Dx
                ///     The new sample rate for the coordinate.
                //-----------------------------------------------------------------
                void SetDx( dx_type Dx );

                //-----------------------------------------------------------------
                /// \brief Set dimension length
                ///
                /// \param[in] Nx
                ///     The new dimension length
                //-----------------------------------------------------------------
                void SetNx( nx_type Nx );

                //----------------------------------------------------------------
                /// \brief Set data set origin.
                ///
                /// \param[in] StartX
                ///     Data set origin.
                //----------------------------------------------------------------
                void SetStartX( startX_type StartX );

                //-----------------------------------------------------------------
                /// \brief Set scale factor in ASCII
                ///
                /// \param[in] UnitX
                ///     The new scale factor description.
                //-----------------------------------------------------------------
                void SetUnitX( const unitX_type& UnitX );

                cmn_streamsize_type
                Bytes( const Common::StreamBase& Stream ) const;

                static nx_type CalcNData( nDim_type NDims, const Data* Dims );

                static const char* StructName( )
                {
                    return "Dimension";
                }

                static INT_4U FrameSpecId( )
                {
                    return 0; // Dimension doesn't have a specific FSI_ ID
                }

            private:
                //-----------------------------------------------------------------
                /// \brief Dimension length.
                //-----------------------------------------------------------------
                nx_type mNx;
                //-----------------------------------------------------------------
                /// \brief Scale factor.
                //-----------------------------------------------------------------
                dx_type mDx;
                //-----------------------------------------------------------------
                /// \brief Scale factor in ASCII.
                //-----------------------------------------------------------------
                unitX_type mUnitX;
                //-----------------------------------------------------------------
                /// \brief Origin.
                //-----------------------------------------------------------------
                startX_type mStartX;
            };

            //===================================================================
            // Inlined methods
            //===================================================================

            //------------------------------------------------------------------
            // Accessors
            //------------------------------------------------------------------

            inline Data::nx_type
            Data::GetNx( ) const
            {
                return mNx;
            }

            inline Data::dx_type
            Data::GetDx( ) const
            {
                return mDx;
            }

            inline Data::startX_type
            Data::GetStartX( ) const
            {
                return mStartX;
            }

            inline const Data::unitX_type&
            Data::GetUnitX( ) const
            {
                return mUnitX;
            }

            //------------------------------------------------------------------
            /// Establish a new value for sample spacing along the coordinate.
            //------------------------------------------------------------------
            inline void
            Data::SetDx( dx_type Dx )
            {
                mDx = Dx;
            }

            inline void
            Data::SetNx( nx_type Nx )
            {
                mNx = Nx;
            }

            inline void
            Data::SetStartX( startX_type StartX )
            {
                mStartX = StartX;
            }

            //------------------------------------------------------------------
            /// Establish a new value for sample spacing along the coordinate.
            //------------------------------------------------------------------
            inline void
            Data::SetUnitX( const unitX_type& UnitX )
            {
                mUnitX = UnitX;
            }


        } // namespace DimensionImpl

    } // namespace Universal

} // namespace FrameCPP

#endif /* FrameCPP_UNIVERSAL__IMPL__Dimension_HH */
