#ifndef _CLI_LIBRARY_LOADER_H_
#define _CLI_LIBRARY_LOADER_H_

#include <dlfcn.h>

#include <experimental/filesystem>
#include <list>

#include <exception/CommonException.h>

namespace cli {

class LibraryLoader {
	class Library {
		std::string m_path;
		void * m_handle;

	public:
		Library ( std::string path ) : m_path ( std::move ( path ) ), m_handle ( nullptr ) {
			load ( );
		}

		Library ( const Library & ) = delete;

		Library ( Library && other ) noexcept : m_path ( std::move ( other.m_path ) ), m_handle ( other.m_handle ) {
			other.m_handle = nullptr;
		}

		Library & operator = ( const Library & ) = delete;
		Library & operator = ( Library && other ) = delete;

		~Library ( ) {
			unload ( );
		}

		void load ( ) {
			if ( ! loaded ( ) )
				m_handle = dlopen ( m_path.c_str ( ), RTLD_NOW );
			if ( ! loaded ( ) )
				throw exception::CommonException ( std::string ( dlerror ( ) ) );
		}

		void unload ( ) {
			if ( loaded ( ) ) {
				dlclose ( m_handle );
				m_handle = nullptr;
			}
		}

		const std::string & path ( ) const {
			return m_path;
		}

		bool loaded ( ) const {
			return m_handle != nullptr;
		}

	};

	static std::list < Library >::iterator find ( const std::string & name );

	static std::list < Library > libraries;

public:
	static void load ( std::string name );
	static void unload ( const std::string & name );
};

} /* namespace cli */

#endif /* _CLI_LIBRARY_LOADER_H_ */
