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

#if HAVE_CONFIG_H
#include <framecpp_config.h>
#endif /* HAVE_CONFIG_H */

#include <unistd.h>

#include <list>
#include <memory>
#include <sstream>

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

#include <boost/shared_ptr.hpp>

#include "ldastoolsal/fstream.hh"

#include "framecpp/FrameCPP.hh"
#include "framecpp/Common/FrameBuffer.hh"
#include "framecpp/Common/FrameStream.hh"
#include "framecpp/GPSTime.hh"

#undef FRAME_SPEC_MIN
#undef FRAME_SPEC_MAX

#define FRAME_SPEC_MIN 4
#define FRAME_SPEC_MAX 8

using LDASTools::AL::filebuf;

using FrameCPP::Common::IFrameStream;
using FrameCPP::Common::OFrameStream;

typedef FrameCPP::Common::FrameBuffer< filebuf > filebuf_type;

using FrameCPP::GPSTime;

typedef boost::shared_ptr< IFrameStream > create_frame_ret_type;

template < int >
create_frame_ret_type create_frame( std::string& Filename );

#define USING_NS( X ) using namespace FrameCPP::Version_##X

GPSTime GTime( 600000000, 0 );

template < typename T >
struct stat_data_container_type : public std::list< boost::shared_ptr< T > >
{
};

#include "FrStatData4.icc"
#include "FrStatData6.icc"
#include "FrStatData7.icc"
#include "FrStatData8.icc"

//=======================================================================
// Testing framework
//=======================================================================
void
test( int                FrameSpec,
      const std::string& Leader,
      INT_4U             Results,
      IFrameStream&      FrameStream,
      const std::string& NamePattern,
      const std::string& DetectorPattern,
      const GPSTime&     Start,
      const GPSTime&     Stop,
      INT_4U             Version )

{
    using FrameCPP::FrStatData;

    FrStatData::Query results;

    FrameStream.ReadFrStatData(
        NamePattern, DetectorPattern, Start, Stop, Version, results );

    BOOST_TEST_MESSAGE( "FrameSpec: " << FrameSpec << ": " << Leader << " Read "
                                      << results.size( ) << " of the expected "
                                      << Results );
    BOOST_CHECK( Results == results.size( ) );
}

static void
version_check( int x )
{
    using FrameCPP::FrStatData;

    std::string filename;

    boost::shared_ptr< IFrameStream > ifs;

    switch ( x )
    {
    case 3: // No TOC support
    case 5: // No support
        break;
    case 4:
        ifs = create_frame< 4 >( filename );
        break;
    case 6:
        ifs = create_frame< 6 >( filename );
        break;
    case 7:
        ifs = create_frame< 7 >( filename );
        break;
    case 8:
        ifs = create_frame< 8 >( filename );
        break;
    default:
        BOOST_TEST_MESSAGE( "Frame specification version "
                            << x << " not supported" );
        BOOST_CHECK( false );
    }

    if ( ifs )
    {
        test( x,
              "Retrieval of all All",
              5,
              *ifs,
              "^.*$",
              "^.*$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::ALL_VERSIONS );

        test( x,
              "Retrieval of all d1",
              2,
              *ifs,
              "^d1:.*$",
              "^d1$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::ALL_VERSIONS );

        test( x,
              "Retrieval of all d2",
              3,
              *ifs,
              "^d2:.*$",
              "^d2$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::ALL_VERSIONS );

        test( x,
              "Retrieval for time 1000 - 1999",
              3,
              *ifs,
              "^.*$",
              "^.*$",
              GTime + 1000,
              GTime + 1999,
              FrStatData::Query::ALL_VERSIONS );

        test( x,
              "Retrieval of latest",
              4,
              *ifs,
              "^.*$",
              "^.*$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::LATEST_VERSION );

        test( x,
              "Retrieval of latest d2",
              2,
              *ifs,
              "^d2:.*$",
              "^d2$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::LATEST_VERSION );

        test( x,
              "Retrieval of any interferometer with a single leter name",
              0,
              *ifs,
              "^.:.*$",
              "^.$",
              GPSTime( 0, 0 ),
              GPSTime( 0, 0 ),
              FrStatData::Query::ALL_VERSIONS );
    }
}

//=======================================================================
// main -- Main testing loop
//=======================================================================
std::ostringstream filename;

BOOST_AUTO_TEST_CASE( StatData )
{
    //---------------------------------------------------------------------
    // Initialize frameCPP library to register all version-specific frame specs
    //---------------------------------------------------------------------
    FrameCPP::Initialize( );

    //---------------------------------------------------------------------
    // Test reading of frames
    //---------------------------------------------------------------------
    for ( int x = FRAME_SPEC_MIN; x <= FRAME_SPEC_MAX; ++x )
    {
        if ( x == 5 )
            continue;
        BOOST_TEST_MESSAGE( "Verifying version " << x << " frames" );
        version_check( x );
    }
}
