/* 
 * © 2007-2008 MySQL AB, 2008-2009 Sun Microsystems, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "stdafx.h"

#include "plugin_manager.h"
#include "editor_base.h"
#include "grt_manager.h"

#include "interfaces/plugin.h"

using namespace bec;
using namespace grt;



static std::string get_args_hash(const grt::BaseListRef &list)
{
  std::string hash;

  for (size_t c= list.count(), i= 0; i < c; i++)
  {
    switch (list.get(i).type())
    {
    case ObjectType:
      hash+= grt::ObjectRef::cast_from(list.get(i)).id();
      break;
    case ListType:
      hash+= get_args_hash(grt::BaseListRef::cast_from(list.get(i)));
      break;
    default:
      hash+= list.get(i).repr();
      break;
    }
  }

  return hash;
}


PluginManagerImpl::PluginManagerImpl(grt::CPPModuleLoader *loader)
  : superclass(loader)
{
  _grtm= GRTManager::get_instance_for(loader->get_grt());
  
  InterfaceImplBase::Register<PluginInterfaceImpl>(loader->get_grt());
}


/** 
 ****************************************************************************
 * @brief Sets the GRT tree path where the plugin registry is located
 *
 * @param plugins_path
 * @param groups_path
 ****************************************************************************
 */
void PluginManagerImpl::set_registry_paths(const std::string &plugins_path,
                                           const std::string &groups_path)
{
  _registry_path= plugins_path;
  _group_registry_path= groups_path;
}


bool PluginManagerImpl::check_plugin_validity(const app_PluginRef &plugin, grt::Module *module)
{
  if (plugin->pluginType() == GUI_PLUGIN_TYPE)
  {
    // not much that can be tested here, maybe check if the dll actually exists
    return true;
  }
  else if (plugin->pluginType() == STANDALONE_GUI_PLUGIN_TYPE
           || plugin->pluginType() == NORMAL_PLUGIN_TYPE)
  {
    // check if the module matches and the function exists
    if (plugin->moduleName() != module->name())
    {
      g_warning("Plugin '%s' from module %s declares moduleName() that doesn't match the module it belongs to",
                plugin->name().c_str(), module->name().c_str());
      return false;
    }
    
    {
      std::string f= plugin->moduleFunctionName();
      if (!module->has_function(f))
      {
         g_warning("Plugin '%s' from module %s has invalid moduleFunctionName '%s'",
                  plugin->name().c_str(), module->name().c_str(), f.c_str());
        return false;
      }
    } 
    return true;
  }
  else
  {
    // 1 more plugin type
    if (plugin->pluginType() == "internal")
      return true;
    
    g_warning("Plugin '%s' from module %s has invalid type '%s'", plugin->name().c_str(), 
              module->name().c_str(), plugin->pluginType().c_str());
  }
  return false;
}


/** 
 ****************************************************************************
 * @brief Refreshes the list of plugins from the list of modules and DLLs
 *
 * Scans the list of registered modules for plugins.
 * Modules exporting plugins must implement PluginInterface.
 * The list of scanned plugins is stored in the GRT tree.
 *
 ****************************************************************************
 */
void PluginManagerImpl::rescan_plugins()
{
  grt::ListRef<app_Plugin> list= get_plugin_list();
  grt::GRT *grt= _grtm->get_grt();

  // get list of modules that implement the plugin interface
  std::vector<Module*> plugin_modules= grt->find_modules_matching("PluginInterface", "");

  _plugin_source_module.clear();
  
  // add all modules to the plugins list
  for (std::vector<Module*>::const_iterator pm= plugin_modules.begin(); pm != plugin_modules.end(); ++pm)
  {
    grt::ListRef<app_Plugin> plist;
    try
    {
      grt::ValueRef result= (*pm)->call_function("getPluginInfo", grt::BaseListRef());
      
      plist= grt::ListRef<app_Plugin>::cast_from(result);
      if (!plist.is_valid() || plist.count() == 0)
      {
        grt->send_warning("Module "+(*pm)->name()+" implements PluginInterface but does not export any plugins", "");
        continue;
      }
    }
    catch (std::exception &exc)
    {
      grt->send_error("Module "+(*pm)->name()+" had an error while executing getPluginInfo: "+exc.what(), 
        "Location: "+(*pm)->path());
      continue;
    }
    
    size_t i, c= plist.count();
    for (i= 0; i < c; i++)
    {
      app_PluginRef plugin= plist[i];
      
      if (_plugin_source_module.find(plugin->name()) != _plugin_source_module.end())
      {
        grt->send_warning("Duplicate plugin name "+*plugin->name(),
                       strfmt("There is more than one plugin with the name %s (in %s and %s).",
                       plugin->name().c_str(), (*pm)->name().c_str(), _plugin_source_module[plugin->name()].c_str()));
        continue;
      }
      
      if (!check_plugin_validity(plugin, *pm))
        continue;

      _plugin_source_module[plugin->name()]= (*pm)->name();

      if (list.is_valid())
        list.insert(plugin);

      if (plugin->groups().count() == 0)
        add_plugin_to_group(plugin, "Others/Ungrouped");
      else
      {
        for (size_t d= plugin->groups().count(), j= 0; j < d; j++)
          add_plugin_to_group(plugin, plugin->groups()[j]);
      }
    }
  }
}




app_PluginGroupRef PluginManagerImpl::get_group(const std::string &group_name)
{
  grt::ListRef<app_PluginGroup> groups;
  app_PluginGroupRef group;

  groups= get_plugin_groups();

  for (unsigned int c= groups.count(), i= 0; i < c; i++)
  {
    if (groups[i]->name() == group_name)
    {
      group= groups[i];
      break;
    }
  }

  return group;
}


void PluginManagerImpl::add_plugin_to_group(const app_PluginRef &plugin, const std::string &group_name)
{
  app_PluginGroupRef group= get_group(group_name);

  if (group.is_valid())
    group->plugins().insert(plugin);
}



/** 
 ****************************************************************************
 * @brief Sets callbacks for handling of GUI plugins
 *
 * @param open callback for opening a plugin
 * @param show callback for showing a plugin
 * @param close callback for closing a plugin
 * 
 ****************************************************************************
 */
void PluginManagerImpl::set_gui_plugin_callbacks(const OpenGUIPluginSlot &open,
                                                 const ShowGUIPluginSlot &show,
                                                 const CloseGUIPluginSlot &close)
{
  _open_gui_plugin_slot= open;
  _show_gui_plugin_slot= show;
  _close_gui_plugin_slot= close;
}




/** 
 ****************************************************************************
 * @brief Adds a list of plugins to the plugin registry
 *
 * @param list of plugins
 * 
 ****************************************************************************
 */
void PluginManagerImpl::register_plugins(grt::ListRef<app_Plugin> plugins)
{
  grt::ListRef<app_Plugin> list= get_plugin_list();
  
  for (unsigned int c= plugins.count(), i= 0; i < c; i++)
    list.insert(plugins[i]);
}




/** 
 ****************************************************************************
 * @brief Return list of registered plugin groups 
 *
 * @return List of plugin groups
 ****************************************************************************
 */
grt::ListRef<app_PluginGroup> PluginManagerImpl::get_plugin_groups()
{
  return grt::ListRef<app_PluginGroup>::cast_from(_grtm->get_grt()->get(_group_registry_path));
}



/** 
 ****************************************************************************
 * @brief Returns list of all registered plugins.
 * 
 * The list of plugins is looked up at the GRT tree path set with
 * set_registry_path()
 *
 * @param group optional name of plugin group
 * 
 * @return List of plugins.
 ****************************************************************************
 */
grt::ListRef<app_Plugin> PluginManagerImpl::get_plugin_list(const std::string &group)
{
  if (group.empty())
    return grt::ListRef<app_Plugin>::cast_from(_grtm->get_grt()->get(_registry_path));
  else
  {
    grt::ListRef<app_Plugin> rlist(_grtm->get_grt()), list;
    std::string left, right;

    if (group.find('/') != std::string::npos)
    {
      left= group.substr(0, group.find('/'));
      right= group.substr(group.find('/')+1);
    }
    else
    {
      left= group;
      right= "*";
    }

    list= grt::ListRef<app_Plugin>::cast_from(_grtm->get_grt()->get(_registry_path));

    for (size_t c= list.count(), i= 0; i < c; i++)
    {
      app_PluginRef plugin(list[i]);
      grt::StringListRef groups(plugin->groups());
      bool found= false;

      for (size_t gc= groups.count(), g= 0; g < gc; g++)
      {
        std::string gstr(groups[g]);
        std::string gleft, gright;
        std::string::size_type pos;

        if ((pos= gstr.find('/')))
        {
          gleft= gstr.substr(0, pos);
          gright= gstr.substr(pos+1);
        }
        else
        {
          gleft= gstr;
          gright= "";
        }

        if ((left=="*" || left == gleft) && (right=="*" || right == gright))
        {
          found= true;
          break;
        }
      }
      if (found)
        rlist.insert(plugin);
    }
    return rlist;
  }
}



bool PluginManagerImpl::check_plugin_input(const app_PluginInputDefinitionRef &def,
                                           const grt::ValueRef &value)
{
  if (def.is_instance(app_PluginFileInput::static_class_name()))
  {
    if (value.is_valid() && value.type() != StringType)
      return false;
  }
  else if (def.is_instance(app_PluginSelectionInput::static_class_name()))
  {
    if (!value.is_valid() || value.type() != ListType)
      return false;

    app_PluginSelectionInputRef sdef(app_PluginSelectionInputRef::cast_from(def));
    grt::ObjectListRef olist(grt::ObjectListRef::cast_from(value));

    for (size_t d= olist.count(), j= 0; j < d; j++)
    {
      grt::ObjectRef value(olist.get(j));
      bool ok= false;

      for (size_t c= sdef->objectStructNames().count(), i= 0; i < c; i++)
      {
        if (value.is_instance(sdef->objectStructNames()[i]))
        {
          ok= true;
          break;
        }
      }
      if (!ok)
        return false;
    }

    std::string card= *sdef->argumentCardinality();
    if (card == "1")
    {
      if (olist.count() != 1)
        return false;
    }
    else if (card == "?")
    {
      if (olist.count() > 1)
        return false;
    }
    else if (card == "+")
    {
      if (olist.count() == 0)
        return false;
    }
    else if (card == "*")
      ;
    else
    {
      if (olist.count() != 1)
        return false;
    }
  }
  else if (def.is_instance(app_PluginObjectInput::static_class_name()))
  {
    if (!value.is_valid() || value.type() != ObjectType)
      return false;

    app_PluginObjectInputRef odef(app_PluginObjectInputRef::cast_from(def));

    if (!grt::ObjectRef::cast_from(value).is_instance(odef->objectStructName()))
      return false;
  }
  else
    return false;

  return true;
}


bool PluginManagerImpl::check_input_for_plugin(const app_PluginRef &plugin, const grt::BaseListRef &args)
{
  if (args.count() != plugin->inputValues().count())
    return false;

  for (size_t c= plugin->inputValues().count(), i= 0; i < c; i++)
  {
    if (!check_plugin_input(plugin->inputValues()[i], args[i]))
      return false;
  }
  return true;
}


app_PluginRef PluginManagerImpl::select_plugin_for_input(const std::string &group, const grt::BaseListRef &args)
{
  ListRef<app_Plugin> plugins= get_plugin_list(group);
  app_PluginRef best_match;
  int rating= -1;

  for (size_t c= plugins.count(), i= 0; i < c; i++)
  {
    app_PluginRef plugin= plugins[i];

    if (check_input_for_plugin(plugin, args) && *plugin->rating() > rating)
    {
      best_match= plugin;
      rating= *plugin->rating();
    }
  }

  return best_match;
}



/** 
 ****************************************************************************
 * @brief Returns list of plugins that can be called on the objects
 *
 *
 * @param objects list of objects
 * @param group optional name of group for filtering (eg: catalog/Editors)
 *
 * @return list of plugins
 ****************************************************************************
 */
std::vector<app_PluginRef> PluginManagerImpl::get_plugins_for_objects(const grt::ObjectListRef &objects,
                                                                   const std::string &group)
{
  std::vector<app_PluginRef> plist;
  grt::ListRef<app_Plugin> plugins;

  plugins= get_plugin_list(group);
  size_t i, c= plugins.count();

  // look for plugins that take 1 ObjectInput arg that is compatible with the obj list
  if (objects.count() == 1)
    for (i= 0; i < c; i++)
    {
      app_PluginRef plugin(plugins[i]);

      if (plugin->inputValues().count() == 1 && 
        plugin->inputValues()[0]->is_instance(app_PluginObjectInput::static_class_name()))
      {
        app_PluginObjectInputRef oinput(app_PluginObjectInputRef::cast_from(plugin->inputValues()[0]));
        std::string struct_name= oinput->objectStructName();
        bool ok= true;

        for (size_t oc= objects.count(), oi= 0; oi < oc; oi++)
        {
          if (!objects[oi].is_instance(struct_name))
          {
            ok= false;
            break;
          }
        }
      
        if (ok)
          plist.push_back(plugin);
      }
    }

  // look for plugins that take selection as input and are compatible with the obj list
  for (i= 0; i < c; i++)
  {
    app_PluginRef plugin(plugins[i]);

    if (plugin->inputValues().count() == 1 && 
      plugin->inputValues()[0]->is_instance(app_PluginSelectionInput::static_class_name()))
    {
      app_PluginSelectionInputRef oinput(app_PluginSelectionInputRef::cast_from(plugin->inputValues()[0]));
      std::string card= *oinput->argumentCardinality();
      grt::StringListRef struct_names(oinput->objectStructNames());
      bool ok= true;

      if (card == "1")
      {
        if (objects.count() != 1)
          continue;
      }
      else if (card == "?")
      {
        if (objects.count() > 1)
          continue;
      }
      else if (card == "+")
      {
        if (objects.count() == 0)
          continue;
      }
      else if (card == "*")
        ;
      else
      {
        if (objects.count() != 1)
          continue;
      }

      for (size_t oc= objects.count(), oi= 0; oi < oc && ok; oi++)
      {
        for (size_t sc= struct_names.count(), si= 0; si < sc && ok; si++)
        {
          if (!objects[oi].is_instance(struct_names[si]))
          {
            ok= false;
            break;
          }
        }
      }

      if (ok)
        plist.push_back(plugin);
    }
  }

  return plist;
}



/** 
 ****************************************************************************
 * @brief Returns list of plugins that can be called on the object
 *
 * @param object
 * @param group optional name of group for filtering
 *
 * @return List of plugins
 ****************************************************************************
 */
std::vector<app_PluginRef> PluginManagerImpl::get_plugins_for_object(const grt::ObjectRef &object,
                                                                  const std::string &group)
{
  grt::ObjectListRef objects(_grtm->get_grt());
  
  objects.insert(object);

  return get_plugins_for_objects(objects, group);
}




/** 
 ****************************************************************************
 * @brief Returns list of plugins in the named group.
 *
 * @param group
 *
 * @return List of plugins
 *
 ****************************************************************************
 */
std::vector<app_PluginRef> PluginManagerImpl::get_plugins_for_group(const std::string &group)
{
  std::vector<app_PluginRef> rlist;
  grt::ListRef<app_Plugin> list= get_plugin_list(group);
  
  for (size_t c= list.count(), i= 0; i < c; i++)
    rlist.push_back(list[i]);
  
  return rlist;
}


/** 
 ****************************************************************************
 * @brief Return named plugin 
 *
 * @param name
 *
 * @return plugin
 ****************************************************************************
 */
app_PluginRef PluginManagerImpl::get_plugin(const std::string &name)
{
  grt::ListRef<app_Plugin> plugins= get_plugin_list();
  
  for (size_t c= plugins.count(), i= 0; i < c; i++)
  {
    if (*plugins[i]->name() == name)
      return plugins[i];
  }
  return app_PluginRef();
}


static std::string make_open_plugin_id(const grt::Module *module,
                                       const std::string &class_name,
                                       const grt::BaseListRef &args)
{
  std::string argshash= get_args_hash(args);
  
  return module->name()+"/"+class_name+"//"+argshash;
}




std::vector<NativeHandle> PluginManagerImpl::get_similar_open_plugins(grt::Module *module, const std::string &class_name, grt::BaseListRef)
{
  std::vector<NativeHandle> handles;

  std::string prefix= module->name()+"/"+class_name+"//";

  for (std::map<std::string, NativeHandle>::const_iterator iter= _open_gui_plugins.begin();
       iter != _open_gui_plugins.end(); ++iter)
  {
    if (iter->first.substr(0, prefix.size()) == prefix)
      handles.push_back(iter->second);
  }

  return handles;
}



std::string PluginManagerImpl::open_gui_plugin(const app_PluginRef &plugin, const grt::BaseListRef &args, GUIPluginFlags flags)
{
  if (!plugin.is_valid())
    throw std::invalid_argument("Attempt to open an invalid plugin");
  
  if (*plugin->pluginType() == GUI_PLUGIN_TYPE)
  {
    if (_grtm->in_main_thread())
      return open_gui_plugin_main(plugin, args, flags);
    else
    {
      GRTDispatcher *disp= _grtm->get_dispatcher();

      // request the plugin to be executed and opened by the frontend in the main thread
      DispatcherCallback<std::string> *cb= new DispatcherCallback<std::string>(sigc::bind<app_PluginRef,grt::BaseListRef,GUIPluginFlags>
                                                               (sigc::mem_fun(this, &PluginManagerImpl::open_gui_plugin_main),
                                                                plugin, args, flags));

      disp->call_from_main_thread(cb, false);
      
      cb->release();

      grt::Module *module= _grtm->get_grt()->get_module(_plugin_source_module[plugin->name()]);
      // build the handle name ourselves
      return make_open_plugin_id(module, plugin->moduleFunctionName(), args);
    }
  }
  else if (*plugin->pluginType() == STANDALONE_GUI_PLUGIN_TYPE)
  {
    if (_grtm->in_main_thread())
      open_standalone_plugin_main(plugin, args);
    else
    {
      GRTDispatcher *disp= _grtm->get_dispatcher();
      
      // request the plugin to be executed and opened by the frontend in the main thread
      DispatcherCallback<void> *cb= new DispatcherCallback<void>(sigc::bind<app_PluginRef,grt::BaseListRef>
                                                                 (sigc::mem_fun(this, &PluginManagerImpl::open_standalone_plugin_main),
                                                                  plugin, args));
      
      disp->call_from_main_thread(cb, false);
      
      cb->release();
    }
  }
  else // a normal plugin implemented by a GRT module
  {
    if (_grtm->in_main_thread())
    {
      _grtm->get_dispatcher()->execute_simple_function("Open normal plugin",
        sigc::bind<app_PluginRef,grt::BaseListRef>(sigc::mem_fun(this, &PluginManagerImpl::open_normal_plugin_grt),
                                                 plugin, args));
    }
    else
      open_normal_plugin_grt(_grtm->get_grt(), plugin, args);
  }
  return "";
}


/** 
 ****************************************************************************
 * @brief Executes the plugin with the given list of arguments
 *
 * If the plugin is a GUI type plugin, the GUI plugin callbacks are executed
 * in the main thread and may return a handle to pass back to the frontend
 * and be processed.
 * 
 * If the plugin is a normal type plugin, it will be just executed in the 
 * GRT thread, as usual.
 *
 * @param plugin object
 * @param args list of 
 *
 * @return An identifier/handle for the opened plugin, if it's a GUI plugin
 * or "" for other types of plugins.
 ****************************************************************************
 */
std::string PluginManagerImpl::open_plugin(const app_PluginRef &plugin, const grt::BaseListRef &args)
{
  return open_gui_plugin(plugin, args, NoFlags);
}


grt::ValueRef PluginManagerImpl::open_normal_plugin_grt(grt::GRT *grt, const app_PluginRef &plugin,
                                                            const grt::BaseListRef &args)
{
  grt::Module *module= _grtm->get_grt()->get_module(plugin->moduleName());

  if (!module)
    throw grt::grt_runtime_error("Cannot execute plugin "+*plugin->name(), "Called module "+*plugin->moduleName()+" not found");

  module->call_function(*plugin->moduleFunctionName(), args);

  return grt::ValueRef();
}


void PluginManagerImpl::open_standalone_plugin_main(const app_PluginRef &plugin, const grt::BaseListRef &args)
{
  grt::Module *module= _grtm->get_grt()->get_module(plugin->moduleName());
  
  if (!module)
    throw grt::grt_runtime_error("Cannot execute plugin "+*plugin->name(), "Called module "+*plugin->moduleName()+" not found");
  
  module->call_function(*plugin->moduleFunctionName(), args);
}



std::string PluginManagerImpl::open_gui_plugin_main(const app_PluginRef &plugin,
                                                    const grt::BaseListRef &args,
                                                    GUIPluginFlags flags)
{
  NativeHandle handle;
  grt::Module *module= _grtm->get_grt()->get_module(_plugin_source_module[plugin->name()]);
  std::string open_plugin_id= make_open_plugin_id(module, plugin->moduleFunctionName(), args);

  if (_open_gui_plugins.find(open_plugin_id) != _open_gui_plugins.end())
  {
    handle= _open_gui_plugins[open_plugin_id];
    _show_gui_plugin_slot(handle);
  }
  else
  {
    grt::Module *module= _grtm->get_grt()->get_module(_plugin_source_module[plugin->name()]);

    // open the editor and get a handle for the GUI object to pass to the frontend
    NativeHandle handle= _open_gui_plugin_slot(_grtm,
                                               module,
                                               *plugin->moduleName(),
                                               *plugin->moduleFunctionName(),
                                               args,
                                               flags);
    if (handle)
    {
      _open_gui_plugins[open_plugin_id]= handle;
      _show_gui_plugin_slot(handle);
    }
  }

  return open_plugin_id;
}



/** 
 ****************************************************************************
 * @brief Shows a previously opened (GUI) plugin.
 *
 * @param handle returned by open_plugin
 * 
 * @return always 0 at the moment
 ****************************************************************************
 */
int PluginManagerImpl::show_plugin(const std::string &handle)
{
  if (_grtm->in_main_thread())
    return show_gui_plugin_main(handle);
  else
  {
    GRTDispatcher *disp= _grtm->get_dispatcher();
    // request the plugin to be executed and opened by the frontend in the main thread

    DispatcherCallback<int> *cb= new DispatcherCallback<int>(sigc::bind<std::string>
                                                             (sigc::mem_fun(this, &PluginManagerImpl::show_gui_plugin_main),
                                                              handle));
    
    disp->call_from_main_thread(cb, false);

    cb->release();

    // return value is ignored atm.

    return 0;
  }
}


int PluginManagerImpl::show_gui_plugin_main(const std::string &handle)
{
  if (_open_gui_plugins.find(handle) != _open_gui_plugins.end())
  {
    NativeHandle hdl= _open_gui_plugins[handle];
    _show_gui_plugin_slot(hdl);
  }

  return 1;
}




/** 
 ****************************************************************************
 * @brief Closes a previously opened (GUI) plugin.
 *
 * @param handle return by open_plugin
 * 
 * @return always 0 at the moment
 ****************************************************************************
 */
int PluginManagerImpl::close_plugin(const std::string &handle)
{
  if (_grtm->in_main_thread())
    return close_gui_plugin_main(handle);
  else
  {
    GRTDispatcher *disp= _grtm->get_dispatcher();
    // request the plugin to be executed and opened by the frontend in the main thread

    DispatcherCallback<int> *cb= new DispatcherCallback<int>(sigc::bind<std::string>
                                                               (sigc::mem_fun(this, &PluginManagerImpl::close_gui_plugin_main),
                                                                handle));
    
    disp->call_from_main_thread(cb, false);

    cb->release();

    // return value is ignored atm.

    return 0;
  }
}


int PluginManagerImpl::close_gui_plugin_main(const std::string &handle)
{
  if (_open_gui_plugins.find(handle) != _open_gui_plugins.end())
  {
    NativeHandle hdl= _open_gui_plugins[handle];
    _close_gui_plugin_slot(hdl);
  }

  return 1;
}



void PluginManagerImpl::forget_gui_plugin_handle(NativeHandle handle)
{
  for (std::map<std::string,NativeHandle>::iterator iter= _open_gui_plugins.begin();
       iter != _open_gui_plugins.end(); ++iter)
  {
    if (iter->second == handle)
    {
      _open_gui_plugins.erase(iter);
      break;
    }
  }
}
