/***************************************************************************
                           cpluginmanager.cpp  -  description
                             -------------------
    begin                : Fri Sep 27 2002
    copyright            : (C) 2002-2003 by Mathias Küster
    email                : mathen@users.berlios.de
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can 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 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/

#include "cpluginmanager.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#ifndef WIN32
#include <dlfcn.h>
#endif

#include "core/cdir.h"
#include "cconfig.h"

/** */
CPluginManager::CPluginManager()
{
	m_pPluginList     = new CList<CPluginObject>();
}

/** */
CPluginManager::~CPluginManager()
{
	SetInstance(0);

	delete m_pPluginList;
	m_pPluginList = 0;
}

typedef ePluginStruct* (*PLUGIN_INIT_FUNC)();
typedef int (*PLUGIN_DEINIT_FUNC)();

/** */
void CPluginManager::Load( CString path )
{
	CDir dir;
	CList<CFileInfo> list;
	CFileInfo *fileinfo;
	PLUGIN_INIT_FUNC dclib_plugin_init;
	CPluginObject * Plugin;

#ifndef WIN32
	void *h;
#else
	HINSTANCE h;
#endif
	// set default path
	if ( path.IsEmpty() )
		path = CConfig::Instance()->GetDCLibPluginPath();

	/* Empty setting to disable plugin load */
	if ( path.IsEmpty() )
		return;

	printf("Searching for plugins in '%s'\n", path.Data());

	// set pluginpath
	dir.SetPath(path);

	// get all files
	if ( dir.ReadEntrys( CDir::Files, &list ) )
	{
		fileinfo = 0;
		while( (fileinfo=list.Next(fileinfo)) != 0 )
		{
#ifdef WIN32
			if ( dir.Extension(fileinfo->name).ToUpper() != "DLL" )
#else
			if ( dir.Extension(fileinfo->name).ToUpper() != "SO" )
#endif
				continue;

			printf("Found plugin: '%s'\n",fileinfo->name.Data());

#ifdef WIN32
			if ( (h = LoadLibraryEx((path+DIRSEPARATOR+fileinfo->name).Data(),NULL,0)) == NULL )
			{
#else
			if ( (h = dlopen( (path+DIRSEPARATOR+fileinfo->name).Data(), RTLD_LAZY )) == NULL )
			{
				printf("Error dlopen %s\n",dlerror());
#endif
				continue;
			}
			
			/* The "fix" for the warning:
			 *
			 * ISO C++ forbids casting between pointer-to-function and pointer-to-object
			 *
			 * as documented in dlopen(3) does not appear to make much
			 * sense and generates a:
			 *
			 * dereferencing type-punned pointer will break strict-aliasing rules
			 *
			 * warning but we already have plenty of those. Trying other things
			 * like reinterpret_cast is no help.
			 */
#ifdef WIN32
			dclib_plugin_init = (PLUGIN_INIT_FUNC) GetProcAddress(h,"dclib_plugin_init");
#else
			*(void**) (&dclib_plugin_init) = dlsym(h, "dclib_plugin_init");
#endif

			if ( dclib_plugin_init == NULL )
			{
#ifndef WIN32
				printf("Error dlsym %s\n",dlerror());
				dlclose(h);
#else
				FreeLibrary(h);
#endif
			}
			else
			{
				Plugin = new CPluginObject();
				Plugin->m_sFileName = path + fileinfo->name;

				Plugin->m_Handle = h;
				Plugin->m_ePluginStruct = dclib_plugin_init();

				Plugin->m_ePluginStruct->init();

				//ps->deinit();

/*
				if ( Plugin->m_eType == eptNONE )
				{
					printf("Error init plugin\n");
#ifndef WIN32
					dlclose(h);
#else
					FreeLibrary(h);
#endif
					delete Plugin;
					continue;
				}
*/
				m_pPluginList->Add(Plugin);
			}
		}
	}
}

/** */
void CPluginManager::InitPlugins()
{
	CPluginObject * plugin = 0;

	while ( (plugin = m_pPluginList->Next(plugin)) != 0 )
	{
		if ( Init(plugin) == false )
		{
			printf("Init Failed\n");
		}
	}
}

/** */
void CPluginManager::DeInitPlugins()
{
	CPluginObject * plugin = 0;

	while ( (plugin = m_pPluginList->Next(0)) != 0 )
	{
		plugin->m_ePluginStruct->deinit();

#ifndef WIN32
		dlclose(plugin->m_Handle);
#else
		FreeLibrary(plugin->m_Handle);
#endif
		m_pPluginList->Remove(plugin);
		delete plugin;
	}
}

/** */
bool CPluginManager::Init( CPluginObject * plugin )
{
	if ( plugin->m_ePluginStruct->m_eType != eptLIB )
	{
		printf("Plugin != LIB not for me ...\n");
		return false;
	}

	return true;
}
