//
// LDASTools frameCPP - Test channel dimensionality reading from frame files
//
// This test verifies that FrVect objects read from frame files maintain correct
// dimensionality information (nDim) through the native C++ interface.
// Helps isolate whether nDim=0 issue is SWIG-specific or affects native C++ as well.
//

#define BOOST_TEST_MAIN
#include <boost/test/included/unit_test.hpp>

#include "framecpp/IFrameStream.hh"
#include "framecpp/OFrameStream.hh"
#include "framecpp/FrAdcData.hh"
#include "framecpp/FrVect.hh"
#include "framecpp/FrTOC.hh"

#include <iostream>
#include <vector>
#include <string>

using namespace FrameCPP;

bool
do_init( )
{
    FrameCPP::Initialize( );
    return ( 1 );
}

static const bool HAS_BEEN_INITIALIZED = do_init( );

// Test frame file path - will be passed as command line argument
static std::string test_frame_path;

// Test channels that should contain time-series data
static const std::vector< std::string > test_channels = {
    "TesT_ZERO_SUPPRESS_0_COMPLEX_8",
    "TesT_RAW_0_COMPLEX_8",
    "TesT_GZIP_1_COMPLEX_8",
    "TesT_RAW_0_REAL_8"
};

BOOST_AUTO_TEST_CASE( test_frame_file_accessibility )
{
    // Use test frame from build directory if no command line argument provided
    if ( test_frame_path.empty( ) )
    {
        // This path should match the frame file used by Python tests
        test_frame_path = "../src/Utilities/Z-R_std_test_frame_ver9-600000000-1.gwf";
    }

    std::cout << "Testing frame file: " << test_frame_path << std::endl;

    FrameCPP::IFrameFStream fs( test_frame_path.c_str( ) );

    BOOST_CHECK_GT( fs.Version( ), 0 );
    std::cout << "Frame version: " << fs.Version( ) << std::endl;

    BOOST_CHECK_GT( fs.GetNumberOfFrames( ), 0 );
    std::cout << "Number of frames: " << fs.GetNumberOfFrames( ) << std::endl;

    auto toc = fs.GetTOC( );
    auto adc_channels = toc->GetADC( );

    BOOST_CHECK_GT( adc_channels.size( ), 0 );
    std::cout << "Total ADC channels: " << adc_channels.size( ) << std::endl;

    // List available test channels
    std::vector< std::string > available_test_channels;
    for ( const auto& channel : test_channels )
    {
        if ( adc_channels.find( channel ) != adc_channels.end( ) )
        {
            available_test_channels.push_back( channel );
        }
    }

    BOOST_CHECK_GT( available_test_channels.size( ), 0 );
    std::cout << "Available test channels: ";
    for ( const auto& channel : available_test_channels )
    {
        std::cout << channel << " ";
    }
    std::cout << std::endl;
}

BOOST_AUTO_TEST_CASE( test_single_channel_dimensionality_native )
{
    FrameCPP::IFrameFStream fs( test_frame_path.c_str( ) );
    auto toc = fs.GetTOC( );
    auto adc_channels = toc->GetADC( );

    // Find first available test channel
    std::string test_channel;
    for ( const auto& channel : test_channels )
    {
        if ( adc_channels.find( channel ) != adc_channels.end( ) )
        {
            test_channel = channel;
            break;
        }
    }

    BOOST_REQUIRE( !test_channel.empty( ) );
    std::cout << "\n=== Testing Native C++ Interface ===" << std::endl;
    std::cout << "Reading channel: " << test_channel << std::endl;

    // Read the FrAdcData object using native C++ interface
    auto adc = fs.ReadFrAdcData( 0, test_channel );
    BOOST_REQUIRE( adc != nullptr );

    BOOST_CHECK_EQUAL( adc->GetName( ), test_channel );
    std::cout << "ADC name: " << adc->GetName( ) << std::endl;
    std::cout << "ADC data container size: " << adc->RefData( ).size( )
              << std::endl;

    BOOST_REQUIRE_GT( adc->RefData( ).size( ), 0 );

    // Access first vector from container using native C++ methods
    auto& data_container = adc->RefData( );
    auto  first_vect = data_container[ 0 ];

    BOOST_REQUIRE( first_vect != nullptr );

    // Report detailed information about the vector
    std::cout << "\nNative C++ Vector Analysis:" << std::endl;
    std::cout << "  Vector name: " << first_vect->GetName( ) << std::endl;
    std::cout << "  Vector type: " << first_vect->GetType( ) << std::endl;
    std::cout << "  Vector nData: " << first_vect->GetNData( ) << std::endl;

    // CRITICAL TEST: Check nDim via native C++ interface
    auto native_ndim = first_vect->GetNDim( );
    std::cout << "  Vector nDim (NATIVE C++): " << native_ndim << std::endl;

    // REGRESSION TEST: nDim should never be 0 for valid time-series data
    BOOST_REQUIRE_GT( native_ndim, 0 );
    std::cout << "  ✓ SUCCESS: Native C++ correctly reads nDim="
              << native_ndim << std::endl;

    // Test dimension access for valid nDim
    for ( size_t i = 0; i < native_ndim; ++i )
    {
        auto dim = first_vect->GetDim( i );
        std::cout << "    Dimension[" << i << "]: nx=" << dim.GetNx( )
                  << ", dx=" << dim.GetDx( )
                  << ", startX=" << dim.GetStartX( )
                  << ", unitX=" << dim.GetUnitX( ) << std::endl;
    }

    // Additional debugging: Check dimension container directly
    auto& dimensions = first_vect->GetDims( );
    std::cout << "  Dimension container size: " << dimensions.size( )
              << std::endl;

    if ( dimensions.size( ) != native_ndim )
    {
        std::cout << "  🚨 INCONSISTENCY: GetNDim()=" << native_ndim
                  << " but dimension container size=" << dimensions.size( )
                  << std::endl;
    }
}

BOOST_AUTO_TEST_CASE( test_multiple_channels_native )
{
    FrameCPP::IFrameFStream fs( test_frame_path.c_str( ) );
    auto toc = fs.GetTOC( );
    auto adc_channels = toc->GetADC( );

    std::cout << "\n=== Testing Multiple Channels (Native C++) ==="
              << std::endl;

    size_t                tested_count = 0;
    std::vector< size_t > ndim_results;

    for ( const auto& channel : test_channels )
    {
        if ( tested_count >= 3 )
            break; // Test first 3 available channels

        if ( adc_channels.find( channel ) != adc_channels.end( ) )
        {
            try
            {
                auto adc = fs.ReadFrAdcData( 0, channel );
                if ( adc && !adc->RefData( ).empty( ) )
                {
                    auto vect = adc->RefData( )[ 0 ];
                    auto ndim = vect->GetNDim( );
                    
                    // REGRESSION TEST: nDim should never be 0
                    BOOST_REQUIRE_GT( ndim, 0 );
                    
                    ndim_results.push_back( ndim );
                    std::cout << "  " << channel << ": nDim = " << ndim
                              << std::endl;
                    tested_count++;
                }
            }
            catch ( const std::exception& e )
            {
                std::cout << "  " << channel << ": ERROR - " << e.what( )
                          << std::endl;
            }
        }
    }

    BOOST_REQUIRE_GT( ndim_results.size( ), 0 );

    // Check consistency across channels
    bool all_same = true;
    auto first_ndim = ndim_results[ 0 ];
    for ( auto ndim : ndim_results )
    {
        if ( ndim != first_ndim )
        {
            all_same = false;
            break;
        }
    }

    if ( all_same )
    {
        std::cout << "✓ All tested channels consistently report nDim = "
                  << first_ndim << std::endl;
    }
    else
    {
        std::cout << "⚠️  Inconsistent nDim values across channels"
                  << std::endl;
    }
}

BOOST_AUTO_TEST_CASE( test_frame_version_analysis )
{
    FrameCPP::IFrameFStream fs( test_frame_path.c_str( ) );

    std::cout << "\n=== Frame Version Analysis ===" << std::endl;
    std::cout << "Frame format version: " << fs.Version( ) << std::endl;

    // Based on our investigation, versions < 3 may have dimension issues
    if ( fs.Version( ) < 3 )
    {
        std::cout << "🚨 CRITICAL: Frame version < 3 detected!" << std::endl;
        std::cout << "   According to FrVectSerialIO.hh line 571, frames with "
                     "version < 3"
                  << std::endl;
        std::cout << "   have their dimension container cleared: "
                     "m_data.dimension.clear();"
                  << std::endl;
        std::cout << "   This explains why nDim=0 (dimension.size() returns 0)."
                  << std::endl;
    }
    else
    {
        std::cout
            << "ℹ️  Frame version >= 3, dimension data should be preserved."
            << std::endl;
    }
}

// Allow frame file path to be set from environment or command line
struct FramePathSetup
{
    FramePathSetup( )
    {
        const char* env_path = std::getenv( "TEST_FRAME_PATH" );
        if ( env_path )
        {
            test_frame_path = env_path;
        }
    }
};

BOOST_GLOBAL_FIXTURE( FramePathSetup );
