/*
 * Copyright 2011-2012 INSA Rennes
 * 
 * This file is part of EIImage.
 * 
 * EIImage 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 3 of the License, or
 * (at your option) any later version.
 * 
 * EIImage is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with EIImage.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "PluginManager.h"

#include <GenericInterface.h>

#include <QMessageBox>
#include <QLibrary>
#include <string>
#include <typeinfo>
#include <sstream>

using namespace genericinterface;
using namespace std;

PluginManager::PluginManager(GenericInterface* gi) {

    _gi = gi;
    QDir directory("Plugins");
    QStringList files = directory.entryList();
    std::cout << files.size() << " files in plugins' directory" << std::endl;
    for(QStringList::iterator it = files.begin(); it != files.end(); ++it) {
        if(QLibrary::isLibrary(*it)) {
            std::cout << "library found : " << it->toStdString() << std::endl;
            loadPlugin((directory.path() + "/") + *it);
        }
    }

    
}

void PluginManager::display(GenericInterface* gi)
{
    
    QMenu* menu = gi->menu("&Plugin");
    
    _loadPluginAction = menu->addAction("&Load plugin");
    _unloadPluginsAction = menu->addAction("&Unload all plugins");
    checkActionsValid();
}

void PluginManager::connect(GenericInterface* gi)
{

    QObject::connect(_loadPluginAction, SIGNAL(triggered()), this, SLOT(choosePlugin()));
    QObject::connect(_unloadPluginsAction, SIGNAL(triggered()), this, SLOT(unloadAllPlugins()));
}

/*void PluginManager::checkActionsValid(QMdiSubWindow* activeWindow) {

	StandardImageWindow* window = (activeWindow) ? dynamic_cast<StandardImageWindow*>(activeWindow->widget()) : NULL;
	if(window) {
      _erosion->setEnabled(true);
    }
    else {
      _erosion->setEnabled(false);
    }
}*/
    
void PluginManager::choosePlugin() {
    QString file = QFileDialog::getOpenFileName(_gi, "Open a file", QString(), "Images (*.dll *.so *.dylib)");
    if(file.size()==0) return;
    std::map<std::string, Plugin*>::iterator it = _plugins.find(file.toStdString());
    if(it != _plugins.end()) {
        unloadPlugin(it->second);
        /*QMessageBox::information (_gi, "Library already loaded", "The library you are trying to load has already been loaded.");*/
        
        //return;
    }

    loadPlugin(file, false);
    
    //QLibrary* library = new QLibrary(file);
    //if(!library->load()) {
        //QMessageBox::critical (_gi, "Error loading plugin", library->errorString());
        //return;
    //}
    //else {
        ////QMessageBox::information (_gi, "Library loaded", "The plugin's library has been loaded successfully");
    //}
    
    //void* ptr = library->resolve("getPlugin");
    
    //if(ptr==0) {
        //QMessageBox::critical (_gi, "Error loading plugin", "Could not find the plugin's entry point \"getPlugin\"");
        //return;
    //} 
    //ostringstream oss;
    
    //typedef Plugin* (*entryPoint)();
    //entryPoint getPlugin = reinterpret_cast<entryPoint>(ptr);
    //Plugin* plugin = getPlugin();
    //if(plugin==NULL) {
        //QMessageBox::critical (_gi, "Error loading plugin", "The getPlugin entry point does not return a valid Plugin");
        //return;
    //}
    
    //vector<Operation*> operations = plugin->getOperations();
    
    
    ////oss << "Found " << operations.size() << " operations in the plugin !";
    //_plugins.insert(pair<string,Plugin*>(file.toStdString(), plugin));
    //emit addPlugin(plugin);
    ////QMessageBox::information (_gi, "Library loaded", oss.str().c_str());
    ////PluginService* pluginService = new PluginService(plugin);
    ////_pluginServices.insert(pair<string,PluginService*>(file.toStdString(), pluginService));
    ////_gi->addNewService(pluginService);
    ////checkActionsValid();
     
    //std::cout << "Plugin " << plugin->getName() << " loaded : " <<  operations.size() << " operations "<< std::endl;
}
void PluginManager::unloadAllPlugins() {
    for(std::map<string, Plugin*>::iterator it = _plugins.begin(); it != _plugins.end(); ++it) {
        emit removePlugin(it->second);
    }
    _plugins.clear();
    checkActionsValid();
}

void PluginManager::unloadPlugin(Plugin* plugin) {
    for(std::map<std::string, Plugin*>::iterator it = _plugins.begin(); it != _plugins.end(); ++it) {
        if(it->second==plugin) {
            //delete it->second;
            //_pluginServices.erase(it);
            ////it = _pluginServices.begin();
            _plugins.erase(it->first);
            emit removePlugin(plugin);
            checkActionsValid();
            return;
        }
    }
}

void PluginManager::checkActionsValid() {
    _unloadPluginsAction->setEnabled(_plugins.size()>0);
}

bool PluginManager::loadPlugin(QString file, bool silent) {

    QLibrary* library = new QLibrary(file);
    if(!library->load()) {
        if(!silent) {
            QMessageBox::critical (_gi, "Error loading plugin", library->errorString());
        }
        return false;
    }
    
    std::cout << file.toStdString() << " loaded" << std::endl;
    
    void* ptr = library->resolve("loadPlugin");
    
    if(ptr==0) {
        if(!silent) {
            QMessageBox::critical (_gi, "Error loading plugin", "Could not find the plugin's entry point \"loadPlugin\"");
        }
        return false;
    } 
    
    std::cout << file.toStdString() << ":  entry point found" << std::endl;
    
    typedef Plugin* (*entryPoint)();
    entryPoint getPlugin = reinterpret_cast<entryPoint>(ptr);
    Plugin* plugin = getPlugin();
    if(plugin==NULL) {
        if(!silent) {
            QMessageBox::critical (_gi, "Error loading plugin", "The getPlugin entry point does not return a valid Plugin");
        }
        return false;
    }
    std::cout << file.toStdString() << ":  got Plugin" << std::endl;
    
    vector<Operation*> operations = plugin->getOperations();
    std::cout << "Plugin " << plugin->getName() << " loaded : " <<  operations.size() << " operations "<< std::endl;
    
    //PluginService* pluginService = new PluginService(plugin);
    _plugins.insert(pair<string,Plugin*>(file.toStdString(), plugin));
    //_gi->addService(pluginService);
    emit addPlugin(plugin);
    checkActionsValid();
    return true;
}