#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "internal.h"
#include "plugin.h"

#ifdef RCC_PLUGINS
# include <dlfcn.h>
# ifndef RTLD_NOW
#  define RTLD_NOW 0
# endif
#endif /* RCC_PLUGINS */


rcc_library_handle rccLibraryOpen(char *filename)
{
#ifdef RCC_PLUGINS
    if (filename) return (rcc_library_handle)dlopen(filename, RTLD_NOW);
#endif /* RCC_PLUGINS */

    return NULL;
}

void rccLibraryClose(rcc_library_handle handle)
{
#ifdef RCC_PLUGINS
    if (handle) dlclose(handle);
#endif /* RCC_PLUGINS */
}

void* rccLibraryFind(rcc_library_handle handle, const char *symbol)
{
#ifdef RCC_PLUGINS
    if ((handle)||(symbol)) return dlsym(handle, symbol);
#endif /* RCC_PLUGINS */

    return NULL;
}


static rcc_plugin_handle_s rcc_engine_handles[RCC_MAX_PLUGINS];

int rccPluginInit() {
    unsigned int i;
    
    for (i=0;i<RCC_MAX_PLUGINS;i++)
	rcc_engine_handles[i].sn = NULL;
    
    return 0;
}

void rccPluginFree() {
    unsigned int i;
    
    for (i=0;i<RCC_MAX_PLUGINS;i++)
	if (rcc_engine_handles[i].sn) {
	    rccLibraryClose(rcc_engine_handles[i].handle);
	    free(rcc_engine_handles[i].sn);
	    rcc_engine_handles[i].sn = NULL;
	}
}


rcc_plugin_handle rccPluginGetHandleByName(rcc_plugin_type type, const char *name) {
    unsigned int i;

    rcc_plugin_handle handles;
    
    if (!name) return NULL;
    
    switch(type) {
	case RCC_PLUGIN_TYPE_ENGINE:
	    handles = rcc_engine_handles;
	break;
	default:
	    return NULL;
    }
    
    for (i=0;i<RCC_MAX_PLUGINS;i++)
	if ((handles[i].sn)&&(!strcasecmp(handles[i].sn, name))) return handles+i;
    
    return NULL;
}

rcc_plugin_handle rccPluginGetFreeHandle(rcc_plugin_type type) {
    unsigned int i;

    rcc_plugin_handle handles;
    
    switch(type) {
	case RCC_PLUGIN_TYPE_ENGINE:
	    handles = rcc_engine_handles;
	break;
	default:
	    return NULL;
    }
    
    for (i=0;i<RCC_MAX_PLUGINS;i++)
	if (!handles[i].sn) return handles+i;
    
    return NULL;
}

rcc_plugin_handle rccPluginLoad(rcc_plugin_type type, const char *name) {
    void *res;
    void *infofunc;
    char *pluginfn;

    rcc_plugin_handle plugin;
    
    if (!name) return NULL;
    
    plugin = rccPluginGetHandleByName(type, name);
    if (plugin) return plugin;
    
    plugin = rccPluginGetFreeHandle(type);
    if (!plugin) return plugin;
    
    switch (type) {
	case RCC_PLUGIN_TYPE_ENGINE:
	    pluginfn = (char*)malloc((32 + strlen(rcc_home_dir) + strlen(name))*sizeof(char));
	    if (!pluginfn) return NULL;
	
	    sprintf(pluginfn, "%s/.rcc/engines/lib%s.so", rcc_home_dir, name);
	    res = dlopen(pluginfn, RTLD_NOW);
	    if (!res) {
		sprintf(pluginfn, LIBRCC_DATA_DIR "/engines/lib%s.so", name);
		res = dlopen(pluginfn, RTLD_NOW);
	    }
	    free(pluginfn);
	    
	    if (res) {
		infofunc = rccLibraryFind(res, "rccGetInfo");
		if (infofunc) {
		    plugin->sn = strdup(name);
		    if (plugin->sn) {
			plugin->handle = (rcc_library_handle)res;
			plugin->info_function = infofunc;
			return plugin;
		    } else rccLibraryClose(res);
		} else rccLibraryClose(res);
	    }
	break;
	default:
	    return NULL;
    }
    
    return NULL;
}


rcc_engine *rccPluginEngineGetInfo(const char *name, const char *language) {
    rcc_plugin_handle handle;
    rcc_plugin_engine_info_function infofunc;
    
    handle = rccPluginLoad(RCC_PLUGIN_TYPE_ENGINE, name);
    if (!handle) return NULL;
    
    infofunc = (rcc_plugin_engine_info_function)(handle->info_function);
    if (!infofunc) return NULL;
    
    return infofunc(language);
}