//
// C++ Implementation: NativePresetFactory
//
// Description: 
//
//
// Author: Carmelo Piccione <carmelo.piccione@gmail.com>, (C) 2008
//
// Copyright: See COPYING file that comes with this distribution
//
//

extern "C" {
#  include <dlfcn.h>
}

#include "NativePresetFactory.hpp"

typedef void Handle;
typedef void DestroyFunctor(Preset*);
typedef Preset * CreateFunctor(const char * url);

class LibraryPreset : public Preset {
public:
	LibraryPreset(Preset * preset, DestroyFunctor * destroyFun) : Preset(preset->name(), preset->author()), _internalPreset(preset), _destroyFunctor(destroyFun) {}
	inline Pipeline & pipeline() { return _internalPreset->pipeline(); }
	inline virtual ~LibraryPreset() { _destroyFunctor(_internalPreset); }
	inline void Render(const BeatDetect &music, const PipelineContext &context) {
		return _internalPreset->Render(music, context);
	}
private:
	Preset * _internalPreset;
	DestroyFunctor * _destroyFunctor;
};

class PresetLibrary {

	public:
		PresetLibrary(Handle * h, CreateFunctor * create, DestroyFunctor * destroy) :
			_handle(h), _createFunctor(create), _destroyFunctor(destroy) {}

		Handle * handle() { return _handle; }
		CreateFunctor * createFunctor() { return _createFunctor; }
		DestroyFunctor * destroyFunctor() { return _destroyFunctor; }

		~PresetLibrary() {
			dlclose(handle());
		}

	private:
		Handle * _handle; 
		CreateFunctor * _createFunctor;
		DestroyFunctor * _destroyFunctor;
	
};

NativePresetFactory::NativePresetFactory() {}

NativePresetFactory::~NativePresetFactory() {

for (PresetLibraryMap::iterator pos = _libraries.begin(); pos != _libraries.end(); ++pos) {
	std::cerr << "deleting preset library" << std::endl;
	delete(pos->second);
}


}

PresetLibrary * NativePresetFactory::loadLibrary(const std::string & url) {

    if (_libraries.count(url))
	return _libraries[url];

    // load the preset library
    void* handle = dlopen(url.c_str(), RTLD_LAZY);
    if (!handle) {
        std::cerr << "[NativePresetFactory] Cannot load library: " << dlerror() << '\n';
        return 0;
    }

    // reset errors
    dlerror();

    // load the symbols
    CreateFunctor * create = (CreateFunctor*) dlsym(handle, "create");
    const char * dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "[NativePresetFactory] Cannot load symbol create: " << dlsym_error << '\n';
        return 0;
    }

    DestroyFunctor * destroy = (DestroyFunctor*) dlsym(handle, "destroy");
    dlsym_error = dlerror();
    if (dlsym_error) {
        std::cerr << "[NativePresetFactory] Cannot load symbol destroy: " << dlsym_error << '\n';
        return 0;
    }

    std::cerr << "[NativePresetFactory] creating preset library from url " << url << std::endl;

    PresetLibrary * library = new PresetLibrary(handle, create, destroy);

    _libraries.insert(std::make_pair(url, library));
    return library;
}


std::unique_ptr<Preset> NativePresetFactory::allocate
	(const std::string & url, const std::string & name, const std::string & author) {
		
	PresetLibrary * library;
	
	if ((library = loadLibrary(url)) == 0)
        return std::unique_ptr<Preset>();
	
	return std::unique_ptr<Preset>(new LibraryPreset
		(library->createFunctor()(url.c_str()), library->destroyFunctor()));
		 
}
