#ifdef HAVE_CONFIG_H
#include <conf_config.h>
#endif
#include <string>
#include <map>

#include <stdlib.h>

#ifdef WIN32
#ifdef HAVE_WINDOWS_H
#include <windows.h>
#endif
#else
#ifdef HAVE_DLFCN_H
#include <dlfcn.h>
#endif
#endif

#include "dynamic.h"
#include <iostream>
bool __module_cache ( void* module, std::string name );
static std::map < std::string, Dynamic_Module*> _modules_;
static std::string error;
bool module_load ( std::string name )
{
	if ( _modules_.find ( name ) != _modules_.end() ) return true;
	void *module;
#ifdef DRIVER_DIR
	std::string drv_dir ( DRIVER_DIR );
#endif
#ifdef WIN32
	module = LoadLibrary ( name.c_str() ) ;
#else
	module = dlopen ( name.c_str() , RTLD_NOW );
#endif
#ifdef DRIVER_DIR
	if ( module==NULL )
	{
		drv_dir+="/";
		drv_dir+=name;
#ifndef WIN32
		error = "Couldn`t load library \"" + name +"\": " + ( std::string ) dlerror() + " trying to load \""+drv_dir+"\"\n";
#else
		error = "Couldn`t load library \"" + name +"\": " + " trying to load \""+drv_dir+"\"\n";
#endif
#ifdef WIN32
		module = LoadLibrary ( drv_dir.c_str() ) ;
#else
		module = dlopen ( drv_dir.c_str() , RTLD_NOW );
#endif
	}
#endif

	if ( module == NULL )
	{
#ifndef WIN32
		error += "Couldn`t load library \"" + name + "\":" + ( std::string ) dlerror()+"\"\n";
#else
		error += "Couldn`t load library \"" + name + "\"\n";
#endif
		return false;
	}
	if ( __module_cache ( module, name ) ) return true;
	else
	{
#ifndef WIN32
		dlclose ( module );
#else
		FreeLibrary ( ( HINSTANCE__* ) module );
#endif
		return false;
	}
	error="";
	return true;
}

Dynamic_Module* module_get ( std::string name )
{
	if ( module_load ( name ) )
	{
		return _modules_[ name ];
	}
	else return 0;
}

bool module_remove ( std::string name )
{
	if ( _modules_.find ( name ) != _modules_.end() )
	{
		void *modul;
		modul = _modules_[ name ] ->_module_get();
#ifndef WIN32
		dlclose ( modul );
#else
		FreeLibrary ( ( HINSTANCE__* ) modul );
#endif
		delete _modules_[ name ];
		_modules_.erase ( name );
		return true;
	}
	return true;
}

bool __module_cache ( void* module, std::string name )
{
	try
	{
		_modules_[ name ] = new Dynamic_Module ( module, name );
		return true;
	}
	catch ( ... )
	{
		return false;
	}
}

bool Dynamic_Module::_symbol_load ( std::string name )
{
	if ( _symbols_.find ( name ) != _symbols_.end() ) return true;
	void *smb;
#ifdef WIN32
	smb = ( void* ) GetProcAddress ( static_cast<HINSTANCE__*> ( _module_ ) , name.c_str() );
#else
	smb = dlsym ( _module_, name.c_str() );
#endif
	if ( smb == NULL )
	{
#ifndef WIN32
		error = dlerror();
#else
		error = "There is no \"" + name + "\" in \"" + _module_name_ + "\"\n";
#endif
		return false;
	}
	error="";
	_symbols_[ name ] = smb;
	return true;
}
void* Dynamic_Module::operator [ ] ( std::string name )
{
	return _symbol_get ( name );
}
bool Dynamic_Module::_symbol_remove ( std::string name )
{
	if ( _symbols_.find ( name ) != _symbols_.end() )
	{
		_symbols_.erase ( name );
		return true;
	}
	else return false;
}

void * Dynamic_Module::_symbol_get ( std::string name )
{
	if ( !_symbol_load ( name ) ) return 0;
	return _symbols_[ name ];
}

const std::string & Dynamic_Module::_module_get_name( ) const
{
	return _module_name_;
}
void *Dynamic_Module::_module_get() const
{
	return _module_;
}
std::string dynamic_error()
{
	return error;
}
std::string dynamic_version()
{
	return VERSION;
}

