//  (C) Copyright Gennadiy Rozental 2005-2014.
//  Distributed under the Boost Software License, Version 1.0.
//  (See accompanying file LICENSE_1_0.txt or copy at 
//  http://www.boost.org/LICENSE_1_0.txt)

//  See http://www.boost.org/libs/test for the library home page.

// Boost.Test
#ifndef BOOST_TEST_DYN_LINK
#define BOOST_TEST_DYN_LINK
#endif
#include <boost/test/unit_test.hpp>

// Boost.Runtime.Param
#include <boost/test/utils/runtime/cla/named_parameter.hpp>
#include <boost/test/utils/runtime/cla/parser.hpp>

namespace rt  = boost::runtime;
namespace cla = boost::runtime::cla;

// STL
#include <iostream>

//_________________________________________________________________//

// System API

namespace dyn_lib {

#if defined(BOOST_WINDOWS) && !defined(BOOST_DISABLE_WIN32) // WIN32 API

#include <windows.h>

typedef HINSTANCE handle;

inline handle
open( std::string const& file_name )
{
    return LoadLibrary( file_name.c_str() );
}

//_________________________________________________________________//

template<typename TargType>
inline TargType
locate_symbol( handle h, std::string const& symbol )
{
    return reinterpret_cast<TargType>( GetProcAddress( h, symbol.c_str() ) );
}

//_________________________________________________________________//

inline void
close( handle h )
{
    if( h )
        FreeLibrary( h );
}

//_________________________________________________________________//

inline std::string
error()
{
    LPTSTR msg = NULL;

    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                   NULL, 
                   GetLastError(), 
                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                   (LPTSTR)&msg, 
                   0, NULL );

    std::string res( msg );

    if( msg )
        LocalFree( msg );

    return res;    
}

//_________________________________________________________________//

#elif defined(BOOST_HAS_UNISTD_H) // POSIX API

#include <dlfcn.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


typedef void* handle;

inline handle
open( std::string const& file_name )
{
    return dlopen( file_name.c_str(), RTLD_LOCAL | RTLD_LAZY );
}

//_________________________________________________________________//

template<typename TargType>
inline TargType
locate_symbol( handle h, std::string const& symbol )
{
    return reinterpret_cast<TargType>( dlsym( h, symbol.c_str() ) );
}

//_________________________________________________________________//

inline void
close( handle h )
{
    if( h )
        dlclose( h );
}

//_________________________________________________________________//

inline std::string
error()
{
    return dlerror();
}

//_________________________________________________________________//

#else

#error "Dynamic library API is unknown"

#endif

} // namespace dyn_lib

//____________________________________________________________________________//

static std::string test_lib_name;
static std::string init_func_name( "init_unit_test" );

dyn_lib::handle test_lib_handle;

bool load_test_lib()
{
    typedef bool (*init_func_ptr)();
    init_func_ptr init_func;

    test_lib_handle = dyn_lib::open( test_lib_name );
    if( !test_lib_handle )
        throw std::logic_error( std::string("Fail to load test library: ")
                                    .append( dyn_lib::error() ) );

    init_func =  dyn_lib::locate_symbol<init_func_ptr>( test_lib_handle, init_func_name );
    
    if( !init_func )
        throw std::logic_error( std::string("Can't locate test initilization function ")
                                    .append( init_func_name )
                                    .append( ": " )
                                    .append( dyn_lib::error() ) );
   
    return (*init_func)();
}

//____________________________________________________________________________//

int main( int argc, char* argv[] )
{
    try {
        cla::parser P;

        P - cla::ignore_mismatch 
            << cla::named_parameter<rt::cstring>( "test" ) - (cla::prefix = "--")
            << cla::named_parameter<rt::cstring>( "init" ) - (cla::prefix = "--",cla::optional);

        P.parse( argc, argv );

        assign_op( test_lib_name, P.get( "test" ), 0 );
        if( P["init"] )
            assign_op( init_func_name, P.get( "init" ), 0 );

        int res = ::boost::unit_test::unit_test_main( &load_test_lib, argc, argv );

        ::boost::unit_test::framework::clear();
        dyn_lib::close( test_lib_handle );

        return res;
    }
    catch( rt::logic_error const& ex ) {
        std::cout << "Fail to parse command line arguments: " << ex.msg() << std::endl;
        return -1;
    }
}

//____________________________________________________________________________//

// EOF
