//
// LDASTools frameCPP - A library implementing the LIGO/Virgo frame
// specification
//
// Copyright (C) 2025 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
//

#include <framecpp_config.h>

#include <mutex>

#include <boost/shared_ptr.hpp>

#include "framecpp/Common/Description.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/Common/FrameSpec.tcc"

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

#include "framecpp/Universal/FrTable.hh"
#include "framecpp/Universal/FrSE.hh"
#include "framecpp/Universal/FrSH.hh"
#include "framecpp/Universal/PTR_STRUCT.hh"

using namespace FrameCPP::Universal;
using FrameCPP::Common::Description;
using FrameCPP::Common::FrameSpec;
using FrameCPP::Common::IStream;
using FrameCPP::Common::OStream;

//=======================================================================
// Static
//=======================================================================

FR_OBJECT_META_DATA_DEFINE_UNIVERSAL( FrTableImpl,
                                      FSI_FR_TABLE,
                                      "FrTable",
                                      "Table Data Structure" )

//=======================================================================
//=======================================================================
FrTable::FrTable( )
{
}

FrTable::FrTable( const FrTable& Source )
{
    setName( Source.GetName( ) );
    AppendComment( Source.GetComment( ) );
    RefColumn( ) = Source.RefColumn( );
}

#if 0
FrTable::FrTable( const FrTableNPS& Source )
    : FrTableNPS( Source )
{
}
#endif /* 0 */

FrTable::FrTable( const name_type& name, nRow_type nrows )
{
    setName( name );
}

FrTable::~FrTable( )
{
}

FrTable&
FrTable::Merge( const FrTable& RHS )
{
    throw Unimplemented( "FrTable& FrTable::Merge( const FrTable& RHS )",
                         DATA_FORMAT_VERSION,
                         __FILE__,
                         __LINE__ );
    return *this;
}

const std::string&
FrTable::GetNameSlow( ) const
{
    return GetName( );
}

bool
FrTable::operator==( const Common::FrameSpec::Object& RHS ) const
{
    return compare( *this, RHS );
}

FrTable&
FrTable::operator+=( const FrTable& RHS )
{
    throw Unimplemented( "FrTable& FrTable::operator+=( const FrTable& RHS )",
                         DATA_FORMAT_VERSION,
                         __FILE__,
                         __LINE__ );
}

//=======================================================================
//               C L A S S I C   I O
//=======================================================================
namespace FrameCPP
{
    namespace Universal
    {
        namespace FrTableImpl
        {

            template <>
            const Common::Description*
            ClassicIO< FrTable >::GetDescription( unsigned int frame_stream_version ) const
            {
                using Common::Description;
                using Universal::PTR_STRUCT;

                Description* description{ nullptr };

                // Helper lambda to populate FrSE fields (captures 'this' for protected access)
                auto populate_frse_fields = [this]( Description& desc, unsigned int version ) {
                    // Common fields for all versions
                    desc( FrSE( "name",
                                Description::Ascii< io_name_type >( ),
                                "Name of this table" ) );
                    desc( FrSE( "comment",
                                Description::Ascii< io_comment_type >( ),
                                "Comment" ) );
                    desc( FrSE( "nColumn",
                                Description::Ascii< nColumn_type >( ),
                                "Number of columns in table" ) );
                    desc( FrSE( "nRow",
                                Description::Ascii< nRow_type >( ),
                                "Number of rows in table." ) );

                    // Version-specific: columnName type differs
                    if ( version >= 5 )
                    {
                        // v5, v6, v7, v8+ use AsciiArray
                        desc( FrSE( "columnName",
                                    Description::AsciiArray< STRING >( "nColumn" ),
                                    "Names of the columns." ) );
                    }
                    else
                    {
                        // v4 uses pointer type
                        desc( FrSE( "columnName",
                                    Description::Ascii< std::string* >( ),
                                    "Names of the columns." ) );
                    }

                    desc( FrSE( "column",
                                PTR_STRUCT::Desc( "FrVect" ),
                                "Next table in linked list" ) );
                    desc( FrSE( "next",
                                PTR_STRUCT::Desc( Data::FrameSpecName( ) ),
                                "Next table in linked list" ) );

                    // Version-specific: v8+ only has chkSum field
                    if ( version >= 8 )
                    {
                        desc( FrSE( "chkSum",
                                    CheckSumDataClass( ),
                                    CheckSumDataComment( ) ) );
                    }
                };

                // Create/locate FrSH and populate FrSE (thread-safe)
                if ( frame_stream_version >= 8 )
                {
                    static Description description_v8;
                    static std::once_flag init_flag_v8;

                    std::call_once( init_flag_v8, [&]( ) {
                        description_v8( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );
                        populate_frse_fields( description_v8, 8 );
                    } );

                    description = &description_v8;
                }
                else if ( frame_stream_version >= 5 )
                {
                    static Description description_v5;
                    static std::once_flag init_flag_v5;

                    std::call_once( init_flag_v5, [&]( ) {
                        description_v5( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );
                        populate_frse_fields( description_v5, 5 );
                    } );

                    description = &description_v5;
                }
                else if ( frame_stream_version >= 4 )
                {
                    static Description description_v4;
                    static std::once_flag init_flag_v4;

                    std::call_once( init_flag_v4, [&]( ) {
                        description_v4( FrSH( Data::FrameSpecName( ),
                                              Data::FrameSpecId( ),
                                              Data::FrameSpecDescription( ) ) );
                        populate_frse_fields( description_v4, 4 );
                    } );

                    description = &description_v4;
                }

                return ( description );
            }

        } // namespace FrTableImpl

    } // namespace Universal
} // namespace FrameCPP
