//
// 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
//

#ifdef FRAMEL_FOUND

#include "FrSampleFrameL.hh"
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <FrameL.h>
#include <boost/serialization/strong_typedef.hpp>

// Define version_type to match FrSample.cc
BOOST_STRONG_TYPEDEF( INT_4U, version_type );

//=============================================================================
// Helper: Create test data for FrameL frames
//=============================================================================
static void
fill_test_data( double* data, int nData )
{
    for ( int i = 0; i < nData; i++ )
    {
        data[ i ] = std::sin( i * 0.1 ) * 100.0 + i * 0.5;
    }
}

//=============================================================================
// test_frame_framel: FrameL-based test frame generation
//=============================================================================
void
test_frame_framel( version_type Version )
{
    std::cout << "Generating test frame with FrameL, version " << Version << "\n";

    // Set FrameL version
    FrLibSetVersion( Version );

    // Create frame with FrameL
    FrameH* frame = FrameNew( const_cast< char* >( "TestFrame" ) );
    if ( !frame )
    {
        throw std::runtime_error( "Failed to create FrameL frame" );
    }

    // Set frame metadata
    frame->run = -1;
    frame->frame = 0;
    frame->GTimeS = 600000000;
    frame->GTimeN = 0;
    frame->dt = 1.0;

    // Create FrAdcData with test pattern
    static constexpr int    TEST_NDATA = 100;
    static constexpr double TEST_SAMPLE_RATE = 16384.0;

    FrAdcData* adc = FrAdcDataNew( frame,
                                   const_cast< char* >( "TestADC" ),
                                   TEST_SAMPLE_RATE,
                                   TEST_NDATA,
                                   -64 ); // double precision
    if ( !adc || !adc->data || !adc->data->dataD )
    {
        FrameFree( frame );
        throw std::runtime_error( "Failed to create FrameL ADC data" );
    }

    // Fill with test data
    fill_test_data( adc->data->dataD, TEST_NDATA );

    // Generate filename
    std::ostringstream filename;
    filename << "Z-R_std_test_framel_ver" << Version << "-" << 600000000 << "-"
             << 1 << ".gwf";

    std::cout << "Writing FrameL frame to: " << filename.str( ) << "\n";

    // Write frame
    FrFile* oFile = FrFileONew( filename.str( ).c_str( ), 0 ); // no compression
    if ( !oFile )
    {
        FrameFree( frame );
        throw std::runtime_error( "Failed to create FrameL output file" );
    }

    int write_result = FrameWrite( frame, oFile );
    FrFileOEnd( oFile );
    FrameFree( frame );

    if ( write_result != FR_OK )
    {
        throw std::runtime_error( "FrameL write failed" );
    }

    std::cout << "FrameL frame written successfully\n";
}

//=============================================================================
// all_type_frame_framel: FrameL-based multi-channel frame generation
//=============================================================================
void
all_type_frame_framel( const std::string&      Type,
                      const channel_set_type& ChannelSet,
                      const GPSTime&          Start,
                      const INT_4U            DeltaT,
                      const INT_4U            Iterations,
                      const version_type      Version,
                      const CommandLine&      CommandLineArgs )
{
    std::cout << "Generating all-type frame with FrameL, version " << Version << "\n";

    // Set FrameL version
    FrLibSetVersion( Version );

    // Create frame with FrameL
    FrameH* frame = FrameNew( const_cast< char* >( Type.c_str( ) ) );
    if ( !frame )
    {
        throw std::runtime_error( "Failed to create FrameL frame" );
    }

    // Set frame metadata
    frame->run = -1;
    frame->frame = 0;
    frame->GTimeS = Start.GetSeconds( );
    frame->GTimeN = Start.GetNanoseconds( );
    frame->dt = static_cast< double >( DeltaT );

    // Create ADC channels based on ChannelSet
    static constexpr int    DEFAULT_NDATA = 1000;
    static constexpr double DEFAULT_SAMPLE_RATE = 16384.0;

    int channel_num = 0;
    for ( INT_4U iteration = 0; iteration < Iterations; ++iteration )
    {
        for ( const auto& channel_name : ChannelSet )
        {
            std::ostringstream full_channel_name;
            full_channel_name << channel_name << "_" << iteration;

            // Create ADC data for this channel
            FrAdcData* adc =
                FrAdcDataNew( frame,
                             const_cast< char* >( full_channel_name.str( ).c_str( ) ),
                             DEFAULT_SAMPLE_RATE,
                             DEFAULT_NDATA,
                             -64 ); // double precision

            if ( adc && adc->data && adc->data->dataD )
            {
                // Fill with test pattern based on channel number
                for ( int i = 0; i < DEFAULT_NDATA; i++ )
                {
                    adc->data->dataD[ i ] =
                        std::sin( ( i + channel_num * 100 ) * 0.1 ) * 100.0 +
                        i * 0.5;
                }
                channel_num++;
            }
        }
    }

    // Generate filename
    std::ostringstream filename;
    filename << "Z-" << Type << "_framel-" << Start.GetSeconds( ) << "-"
             << DeltaT << ".gwf";

    std::cout << "Writing FrameL frame to: " << filename.str( ) << "\n";

    // Write frame
    FrFile* oFile = FrFileONew( filename.str( ).c_str( ), 0 ); // no compression
    if ( !oFile )
    {
        FrameFree( frame );
        throw std::runtime_error( "Failed to create FrameL output file" );
    }

    int write_result = FrameWrite( frame, oFile );
    FrFileOEnd( oFile );
    FrameFree( frame );

    if ( write_result != FR_OK )
    {
        throw std::runtime_error( "FrameL write failed" );
    }

    std::cout << "FrameL frame written successfully with " << channel_num
              << " channels\n";
}

#endif // FRAMEL_FOUND
