/**
        \file ADM_pluginLoad.cpp
        \brief Interface for dynamically loaded audio decoder

        There are 2 known problem here
        1: The destructor is called instead of calling destroy in the class factory
        2: Memory leak, ADM_audioPlugins is not destroyed as of today

*/


/***************************************************************************
 *                                                                         *
 *   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 "ADM_default.h"
#include "ADM_ad_plugin.h"
#include "DIA_fileSel.h"
#include "ADM_dynamicLoading.h"
#include <vector>

#if 1
#define aprintf printf
#else
#define aprintf(...) {}
#endif
/*  Exported functions */
uint32_t ADM_ad_getNbFilters(void);
bool     ADM_ad_getFilterInfo(int filter, const char **name,
                                uint32_t *major,uint32_t *minor,uint32_t *patch);

/**
 *
 */
class ADM_ad_plugin : public ADM_LibWrapper
{
	public:
                ADM_ad_CreateFunction		*create;
                ADM_ad_DeleteFunction		*destroy;
                ADM_ad_SupportedFormat		*supportedFormat;
                ADM_ad_GetApiVersion		*getApiVersion;
                ADM_ad_GetDecoderVersion	*getDecoderVersion;
                ADM_ADM_ad_GetInfo		*getInfo;
                std::string			name;

		ADM_ad_plugin(const char *file) : ADM_LibWrapper()
		{
			initialised = (loadLibrary(file) && getSymbols(6,
				&create, "create",
				&destroy, "destroy",
				&supportedFormat, "supportedFormat",
				&getApiVersion, "getApiVersion",
				&getDecoderVersion, "getDecoderVersion",
				&getInfo, "getInfo"));
		};
};

std::vector<ADM_ad_plugin *> ADM_audioPlugins;
/**
 * 	\fn tryLoadingAudioPlugin
 *  \brief try to load the plugin given as argument..
 */
static uint8_t tryLoadingAudioPlugin(const char *file)
{
	ADM_ad_plugin *plugin = new ADM_ad_plugin(file);

	if (!plugin->isAvailable())
	{
		printf("[ADM_ad_plugin] Unable to load %s\n", ADM_getFileName(std::string(file)).c_str());
		goto Err_ad;
	}

	// Check API version
	if (plugin->getApiVersion() != AD_API_VERSION)
	{
		ADM_warning("[ADM_ad_plugin] File %s has API version too old (%d vs %d)\n",
			ADM_getFileName(std::string(file)).c_str(), plugin->getApiVersion(), AD_API_VERSION);
		goto Err_ad;
	}

	// Get infos
	uint32_t major, minor, patch;

	plugin->getDecoderVersion(&major, &minor, &patch);
	plugin->name = ADM_getFileName(std::string(file));

	ADM_info("[ADM_ad_plugin] Plugin loaded version %d.%d.%d, name %s, desc: %s\n",
		major, minor, patch, plugin->name.c_str(), plugin->getInfo());

	ADM_audioPlugins.push_back(plugin);

	return 1;

Err_ad:
	delete plugin;
	return 0;
}
/**
    \fn ADM_ad_getNbFilters
    \brief returns the # of loaded audio decoder
*/
uint32_t ADM_ad_getNbFilters(void)
{
    return (uint32_t )ADM_audioPlugins.size();
}
/**
    \fn ADM_ad_getFilterInfo
    \brief returns infos about a given filter
    @param filter [in] # of the filter we are intereseted in, between 0 & ADM_ad_getNbFilters
    @param name [out] Name of the decoder plugin
    @param major, minor,patch [out] Version number
    @return true
*/
bool ADM_ad_getFilterInfo(int filter, std::string &name, uint32_t *major,uint32_t *minor,uint32_t *patch)
{

        ADM_assert(filter>=0 && filter<ADM_audioPlugins.size());

    	ADM_ad_plugin *a=ADM_audioPlugins[filter];
        a->getDecoderVersion(major, minor, patch);

        name=a->getInfo();
        return 1;
}

/**
 * 	\fn ADM_ad_loadPlugins
 *  \brief load all audio plugins
 */
uint8_t ADM_ad_loadPlugins(const char *path)
{
	std::vector<std::string> files;
	ADM_info("Scanning directory %s\n",path);

	if(!buildDirectoryContent(path, &files, SHARED_LIB_EXT))
	{
		ADM_warning("Cannot open plugin directory\n");
		return 0;
	}

	for(int i=0;i<files.size();i++)
		tryLoadingAudioPlugin(files.at(i).c_str());

	ADM_info("Scanning done, found %d audio decoders\n", (int)ADM_audioPlugins.size());
	return 1;
}
/**
 * 	\fn ADM_ad_searchCodec
 *  \brief Try to instantiate a codec that can decode the given stuff
 */
ADM_Audiocodec *ADM_ad_searchCodec(uint32_t fourcc,	WAVHeader *info,uint32_t extraLength,uint8_t *extraData)
{
    int best=0;
    int index=-1;
	for(int i=0;i<ADM_audioPlugins.size();i++)
	{
		ADM_ad_plugin *a=ADM_audioPlugins[i];
		ADM_assert(a);
		ADM_assert(a->supportedFormat);
		
        int score=a->supportedFormat(fourcc);
        ADM_info("[ADM_ad_plugin]Format 0x%x : probing %s score %d\n",fourcc,a->name.c_str(),score);
        if(score>best)
        {
            index=i;
            best=score;
        }
	}
    if(index!=-1 && best >0)
    {
        ADM_ad_plugin *a=ADM_audioPlugins[index];
        ADM_assert(a->create);
        return a->create(fourcc, info,extraLength,extraData);
    }
	return NULL;
}
/**
    \fn ADM_ad_cleanup
*/
bool ADM_ad_cleanup(void)
{
    ADM_info("Purging audio decoder\n");
    for(int i=0;i<ADM_audioPlugins.size();i++)
    {
        ADM_ad_plugin *a=ADM_audioPlugins[i];
        delete a;
        ADM_audioPlugins[i]=NULL;
    }
    ADM_audioPlugins.clear();
    return true;
}   
//EOF
