//
// LDASTools AL - A library collection to provide an abstraction layer
//
// Copyright (C) 2018 California Institute of Technology
//
// LDASTools AL 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 AL 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 <ldastoolsal_config.h>

#if HAVE_SYS_SYSTEMINFO_H
#include <sys/systeminfo.h>
#endif /* HAVE_SYS_SYSTEMINFO_H */

#include <cstdlib>
#include <dlfcn.h>

#include <sstream>
#include <stdexcept>

#include "ldastoolsal/SOLoader.hh"

static std::string find_lib( const std::string& Path,
                             const std::string& LibName );

namespace LDASTools
{
    namespace AL
    {
        SOLoader::SOLoader( const std::string& Path,
                            const std::string& LibName,
                            int                Type )
            : m_handle( (handle_type)NULL )
        {
            std::string lib = find_lib( Path, LibName );

            m_handle = dlopen( lib.c_str( ), RTLD_NOW );
            if ( ( m_handle == (handle_type)NULL ) &&
                 ( Type == SOLOADER_MANDITORY ) )
            {
                std::stringstream msg;

                msg << "Unable to load " << lib;
                throw std::runtime_error( msg.str( ).c_str( ) );
            }
        }

        SOLoader::~SOLoader( )
        {
            if ( m_handle )
            {
                dlclose( m_handle );
                m_handle = (handle_type)NULL;
            }
        }

        SOLoader::function_type
        SOLoader::GetFunction( const std::string& Function )
        {
            //-------------------------------------------------------------------
            // :TRICKY: This union is needed to avoid warning about converting
            // :TRICKY:    from void* to void (*)(void).
            //-------------------------------------------------------------------
            union
            {
                function_type f;
                symbol_type   s;
            } retval;

            if ( m_handle )
            {
                retval.s = dlsym( m_handle, Function.c_str( ) );
            }
            else
            {
                retval.s = (symbol_type)NULL;
            }
            return retval.f;
        }

        SOLoader::symbol_type
        SOLoader::GetSymbol( const std::string& Symbol )
        {
            symbol_type retval = ( symbol_type )( NULL );

            if ( m_handle )
            {
                retval = ( symbol_type )( dlsym( m_handle, Symbol.c_str( ) ) );
            }
            return retval;
        }
    } // namespace AL
} // namespace LDASTools

static std::string
find_lib( const std::string& Path, const std::string& LibName )
{
    std::ostringstream retval;

    retval << Path;
#ifdef SI_ARCHITECTURE_64
    if ( sizeof( void ( * )( ) ) == 8 )
    {
        // Running as a 64-bit application
        char buffer[ 256 ];

        sysinfo( SI_ARCHITECTURE_64, buffer, sizeof( buffer ) );
        retval << "/" << buffer;
    }
#endif /* SI_ARCHITECTURE_64 */
    retval << "/" << LibName;

    return retval.str( );
}
