/*
 * TV-Browser
 * Copyright (C) 04-2003 Martin Oberhauser (martin@tvbrowser.org)
 *
 * 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * CVS information:
 *  $RCSfile$
 *   $Source$
 *     $Date: 2014-06-17 01:34:56 +0200 (Di, 17 Jun 2014) $
 *   $Author: ds10 $
 * $Revision: 8144 $
 */
package tvbrowser.core;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.Icon;
import javax.swing.ImageIcon;

import org.apache.commons.lang3.StringUtils;

import tvbrowser.core.plugin.AbstractPluginProxy;
import tvbrowser.core.plugin.BeanShellPluginProxy;
import tvbrowser.core.plugin.JavaPluginProxy;
import tvbrowser.core.plugin.PluginBaseInfo;
import tvbrowser.core.plugin.PluginManagerImpl;
import tvbrowser.core.plugin.PluginProxy;
import tvbrowser.core.plugin.PluginProxyManager;
import tvbrowser.core.tvdataservice.DefaultTvDataServiceProxy;
import tvbrowser.core.tvdataservice.TvDataServiceProxy;
import tvbrowser.core.tvdataservice.TvDataServiceProxyManager;
import util.exc.ErrorHandler;
import util.exc.TvBrowserException;
import util.io.IOUtilities;
import devplugin.ActionMenu;
import devplugin.Plugin;
import devplugin.PluginInfo;
import devplugin.Version;

/**
 * The PluginLoader loads all plugins and assigns each plugin to
 * the appropriate manager (TvDataServiceProxyManager or PluginProxyManager)
 */
public class PluginLoader {

  private static final String[] IGNORED_PLUGINS = new String[]{"FavoritesPlugin.jar", "ReminderPlugin.jar", "ProgramInfo.jar", "SearchPlugin.jar", "ShowviewPlugin.jar"};

  private static final String PLUGIN_PROXY_EXTENSION = ".jar.proxy";

	private static final String PLUGIN_INSTALL_EXTENSION = ".inst";

  /** The logger for this class */
  private static final Logger mLog
      = Logger.getLogger(PluginLoader.class.getName());

  private static PluginLoader mInstance;

  /** The name of the directory where the plugins are located in TV-Browser 2.1 and later */
  private static String PLUGIN_DIRECTORY = "plugins";

  private HashSet<String> mSuccessfullyLoadedPluginFiles;

  private HashMap<Object, File> mDeleteablePlugin;

  private ArrayList<PluginProxy> loadedProxies;

  private ArrayList<String> mNewInstalledPlugins = new ArrayList<String>();
  
  private boolean mShowMouseInfo;

  private PluginLoader() {
    mSuccessfullyLoadedPluginFiles = new HashSet<String>();
    mDeleteablePlugin = new HashMap<Object, File>();
    mShowMouseInfo = false;
  }

  public static PluginLoader getInstance() {
    if (mInstance == null) {
      mInstance = new PluginLoader();
    }
    return mInstance;
  }

  /**
   * Installs all plugins that could not be installed the last time, because an
   * old version was in use.
   */
  public void installPendingPlugins() {
    File[] installableFiles = new File(Settings.propPluginsDirectory.getString()).listFiles(new FilenameFilter() {
      @Override
      public boolean accept(File dir, String fileName) {
        return fileName.endsWith(PLUGIN_INSTALL_EXTENSION);
      }
    });
    if (installableFiles == null || installableFiles.length == 0) {
      // Nothing to do
      return;
    }

    // Install all pending plugins
    for (File file : installableFiles) {
      // This plugin wants to be installed
      String fileName = file.getAbsolutePath();
      String oldFileName = fileName.substring(0, fileName.length() - PLUGIN_INSTALL_EXTENSION.length());
      File oldFile = new File(oldFileName);

      // delete the old proxy, this will force loading of the new plugin (even if it's not active)
      String oldProxyName = getProxyFileName(oldFile);
      File oldProxy = new File(oldProxyName);
      if (oldProxy.exists()) {
        deletePluginProxy(oldProxy);
      }
      String oldIconName = getProxyIconFileName(oldFile);
      File oldIcon = new File(oldIconName);
      if (oldIcon.exists()) {
        deletePluginProxy(oldIcon);
      }

      // Delete the old file
      deletePluginProxy(oldFile);

      // Rename the file, so the PluginLoader will install it later
      if (!file.renameTo(oldFile)) {
        mLog.warning("Installing pending plugin failed: " + fileName);
      }

      mNewInstalledPlugins.add(oldFileName);
    }
  }

  private PluginProxy loadProxy(File proxyFile) {
    String lcFileName = proxyFile.getName().toLowerCase();
    if (!lcFileName.endsWith(".proxy")) {
      mLog.warning("not a valid proxy file "+proxyFile.getAbsolutePath());
      return null;
    }
    if (proxyFile.canRead()) {
      JavaPluginProxy proxy = readPluginProxy(proxyFile);
      if (proxy != null) {
        PluginProxyManager.getInstance().registerPlugin(proxy);
        if (new File(proxy.getPluginFileName()).getParentFile().equals(new File(Settings.propPluginsDirectory.getString()))) {
          File pluginFile = new File(proxy.getPluginFileName());
          mDeleteablePlugin.put(proxy, pluginFile);
        }
        return proxy;
      }
    }
    return null;
  }


  /**
   * Loads the plugin from the file system
   * @param pluginFile File to load
   * @param deleteable is the Plugin deleteable
   */
  public Object loadPlugin(File pluginFile, boolean deleteable) {
    Object plugin = null;
    String lcFileName = pluginFile.getName().toLowerCase();
    if (mSuccessfullyLoadedPluginFiles.contains(lcFileName)) {
      mLog.warning("cannot load plugin "+pluginFile.getAbsolutePath()+" - already loaded");
      return null;
    }

    try {
      if (lcFileName.endsWith(".jar")) {
        plugin = loadJavaPlugin(pluginFile);
      }
      else if (lcFileName.endsWith(".bsh")) {
        plugin = loadBeanShellPlugin(pluginFile);
      }
      else {
        mLog.warning("Unknown plugin type: " + pluginFile.getAbsolutePath());
      }

      if (plugin instanceof Plugin) {
        // check if the proxy is already loaded, but the plugin was not loaded yet
        JavaPluginProxy javaplugin = (JavaPluginProxy) PluginProxyManager.getInstance().getPluginForId(JavaPluginProxy.getJavaPluginId((Plugin) plugin));
        if (javaplugin != null) {
          javaplugin.setPlugin((Plugin) plugin, pluginFile.getPath());
        }
        // it was not yet loaded, so create new proxy
        else {
          javaplugin = new JavaPluginProxy((Plugin)plugin, pluginFile.getPath());
          PluginProxyManager.getInstance().registerPlugin(javaplugin);
        }
        if (mNewInstalledPlugins.contains(pluginFile.getAbsolutePath())) {
          // add this plugin to the icon settings for program panels
          String iconText = ((Plugin)plugin).getProgramTableIconText();
          if (iconText != null && !iconText.isEmpty()) {
            if (!Settings.propProgramTableIconPlugins.containsItem(((Plugin)plugin).getId())) {
              Settings.propProgramTableIconPlugins.addItem(((Plugin)plugin).getId());
            }
          }
          
          if(!Settings.propKnownContextMenuPlugins.containsItem(((Plugin)plugin).getId())) {
            ActionMenu test = ((Plugin)plugin).getContextMenuActions(PluginManagerImpl.getInstance().getExampleProgram());
            
            if(test != null) {
              mShowMouseInfo = true;
            }
            
            Settings.propKnownContextMenuPlugins.addItem(((Plugin)plugin).getId());
          }
        }
        if (deleteable) {
          mDeleteablePlugin.put(javaplugin, pluginFile);
        }

        saveProxyInfo(pluginFile, javaplugin);
      }
      else if (plugin instanceof AbstractPluginProxy) {
        PluginProxyManager.getInstance().registerPlugin((AbstractPluginProxy)plugin);
        if (deleteable) {
          mDeleteablePlugin.put(plugin, pluginFile);
        }
      }
      else if (plugin instanceof devplugin.AbstractTvDataService) {
        TvDataServiceProxy proxy = new DefaultTvDataServiceProxy((devplugin.AbstractTvDataService)plugin);
        TvDataServiceProxyManager.getInstance().registerTvDataService(proxy);
        if (deleteable) {
          mDeleteablePlugin.put(proxy, pluginFile);
        }
      }

      if(plugin != null) {
        String version = null;
        
        if(plugin instanceof Plugin || plugin instanceof devplugin.AbstractTvDataService) {
          try {
            Method m = plugin.getClass().getMethod("getVersion", new Class<?>[0]);
            Object o = m.invoke(plugin, new Object[0]);
            
            if(o instanceof Version) {
              version = ((Version)o).toString();
            }
            
          }catch(Exception e1) {}
        }
        
        mSuccessfullyLoadedPluginFiles.add(lcFileName);
        mLog.info("Loaded plugin "+pluginFile.getAbsolutePath() + (version != null ? " - " + version : ""));
      }
    }catch (Throwable thr) {
      mLog.log(Level.WARNING, "Loading plugin file failed: "
          + pluginFile.getAbsolutePath(), thr);
      thr.printStackTrace();
    }
    return plugin;
  }


  /**
   * read the contents of a proxy file to get the necessary
   * information about the plugin managed by this proxy to recreate
   * the proxy without the plugin actually being loaded
   *
   * @param proxyFile
   * @return pluginProxy
   */
  private JavaPluginProxy readPluginProxy(File proxyFile) {
    DataInputStream in = null;

    try {
      in = new DataInputStream(new BufferedInputStream(new FileInputStream(proxyFile)));

      String name = in.readUTF();
      String author = in.readUTF();
      String description = in.readUTF();
      String license = in.readUTF();

      DummyPlugin.setCurrentVersion(new Version(in));

      String pluginId = in.readUTF();
      in.readLong(); // file size is unused
      String lcFileName = in.readUTF();
      String category = in.readUTF();
      in.close();
      // check existence of plugin file
      File pluginFile = new File(lcFileName);
      if (!pluginFile.canRead()) {
        deletePluginProxy(proxyFile);
        return null;
      }
      
      // everything seems fine, create plugin proxy and plugin info
      PluginInfo info = new PluginInfo(DummyPlugin.class, name, description, author, license);
      // now get icon
      String iconFileName = getProxyIconFileName(proxyFile);
      return new JavaPluginProxy(info, lcFileName, pluginId, iconFileName,category);
    } catch (Exception e) {
      if(in != null) {
        try {
          in.close();
        } catch (IOException e1) {
          // ignore
        }
      }
      // delete proxy on read error, maybe the format has changed
      deletePluginProxy(proxyFile);
      return null;
    }
  }

  private void deletePluginProxy(File proxyFile) {
    String iconName = getProxyIconFileName(proxyFile);
    proxyFile.delete();
    File iconFile = new File(iconName);
    iconFile.delete();
  }

  /**
   * Saves the information of a plugin to disk, so it does not need to be loaded next time
   * @param pluginFile full plugin file name
   * @param proxy proxy of the plugin
   */
  private void saveProxyInfo(File pluginFile, JavaPluginProxy proxy) {
    try {
      String proxyFileName = getProxyFileName(pluginFile);
      DataOutputStream out = new DataOutputStream(new
          BufferedOutputStream(new FileOutputStream(proxyFileName)));

      PluginInfo info = proxy.getInfo();
      out.writeUTF(info.getName());
      out.writeUTF(info.getAuthor());
      out.writeUTF(info.getDescription());
      String license = info.getLicense();
      if (license == null) {
        license = "";
      }
      out.writeUTF(license);

      info.getVersion().writeData(out); //write version

      out.writeUTF(proxy.getId());
      out.writeLong(pluginFile.length());
      out.writeUTF(proxy.getPluginFileName());
      out.writeUTF(proxy.getPluginCategory());
      out.close();
      // also store the plugin icon, if it is not yet available
      String iconFileName = getProxyIconFileName(pluginFile);
      File iconFile = new File(iconFileName);
      if (!iconFile.exists()) {
        Icon pluginIcon = proxy.getPluginIcon();
        if (pluginIcon != null && pluginIcon instanceof ImageIcon) {
          IOUtilities.writeImageIconToFile((ImageIcon)pluginIcon, "png", iconFile);
        }
      }
    } catch (Exception e) {
    }
  }

  /**
   * get the file name of the proxied icon for a plugin
   * @param pluginFile
   * @return icon file name
   */
  private String getProxyIconFileName(File pluginFile) {
    String name = pluginFile.getName();
    name = StringUtils.substringBefore(name,".");
    return Settings.getUserSettingsDirName() + File.separatorChar + name + ".icon.png";
  }

  /**
   * file name of the proxy file for a plugin
   *
   * @param pluginFile
   * @return proxy file name
   */
  private String getProxyFileName(File pluginFile) {
    return Settings.getUserSettingsDirName() + File.separatorChar + pluginFile.getName() + ".proxy";
  }

  /**
   * Loads all plugins within the specified folder
   * @param folder specific Folder
   * @param deleteable True if the Plugins in this Folder are deleteable
   */
  private void loadPlugins(File folder, boolean deleteable) {
    // check for plugin proxies only one time per run
    if (loadedProxies == null) {
      loadedProxies = new ArrayList<PluginProxy>();
      final String[] deactivatedPluginArr = Settings.propDeactivatedPlugins.getStringArray();

      // only check proxies if at least one plugin is not active
      if (deactivatedPluginArr != null && deactivatedPluginArr.length > 0) {
        File settingsDir = new File(Settings.getUserSettingsDirName());
        File[] proxyFiles = settingsDir.listFiles(new FilenameFilter() {
          public boolean accept(File dir, String name) {
            if (!name.endsWith(PLUGIN_PROXY_EXTENSION)) {
              return false;
            }
            String mainName = name.substring(0, name.length() - PLUGIN_PROXY_EXTENSION.length()).toLowerCase();
            for (String deactivatedId : deactivatedPluginArr) {
            	if (deactivatedId.contains(mainName)) {
                return true;
              }
            }
            return false;
          }});
        if (proxyFiles != null) {
          for (File proxyFile : proxyFiles) {
            PluginProxy proxy = loadProxy(proxyFile);
            if (proxy != null) {
              loadedProxies.add(proxy);
              mLog.info("Loaded plugin proxy " + proxyFile);
            }
            else {
              mLog.warning("Failed loading plugin proxy " + proxyFile);
            }
          }
        }
      }
    }

    File[] fileArr = folder.listFiles(new FilenameFilter(){
      public boolean accept(File dir, String fileName) {
        for (String ignored : IGNORED_PLUGINS) {
          if (fileName.equalsIgnoreCase(ignored)) {
            return false;
          }
        }
        return true;
      }
    });
    if (fileArr == null) {
      return;
    }

    // remove old cleverepg service, if new one is found
    File oldService = null;
    ArrayList<File> files = new ArrayList<File>(Arrays.asList(fileArr));
    for (File file : fileArr) {
      if (file.getName().equalsIgnoreCase("CleverEPGDataService3.jar")) {
        for (File file2 : files) {
          if (file2.getName().equalsIgnoreCase("CleverEPGDataService.jar")) {
            oldService = file2;
            break;
          }
        }
      }
    }
    if (oldService != null) {
      files.remove(oldService);
    }

    for (File file : files) {
      boolean load = true;
      for (PluginProxy proxy : loadedProxies) {
        if (proxy.getPluginFileName().equalsIgnoreCase(file.getPath())) {
          load = false;
          break;
        }
      }
      if (load) {
        loadPlugin(file, deleteable);
      }
    }
  }

  public void loadAllPlugins() {

    /* 0) delete all plugins the user doesn't want anymore */

    String[] files = Settings.propDeleteFilesAtStart.getStringArray();

    if ((files != null) && (files.length > 0)) {
      for (String file : files) {
        try {
          mLog.info("Deleting " + file);
          new File(file).delete();
        } catch (Exception e) {
          e.printStackTrace();
        }
      }

      Settings.propDeleteFilesAtStart.setStringArray(new String[0]);
    }


    /* 1) load all plugins from the plugins folder in the user's home directory */
    String pluginsFolderName = Settings.propPluginsDirectory.getString();
    File f = new File(pluginsFolderName);
    boolean success = true;
    if (!f.exists()) {
      if (!f.mkdirs()) {
        mLog.warning("Could not create plugins folder "+f.getAbsolutePath());
        success = false;
      }
    }
    if (success) {
      loadPlugins(f, true);
    }

    /* 2) load the plugins from the plugins folder */
    loadPlugins(new File(PluginProxyManager.PLUGIN_DIRECTORY), false);

    /* 3) load the plugins from the tvdataservice folder */
    loadPlugins(new File(TvDataServiceProxyManager.PLUGIN_DIRECTORY), false);

  }




  private Object loadJavaPlugin(File jarFile) throws TvBrowserException {
    Object plugin = null;

    // Create a class loader for the plugin
    ClassLoader classLoader;
    ClassLoader classLoader2 = null;

    try {
      URL[] urls = new URL[] { jarFile.toURI().toURL() };
      classLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader());

      try {
        if(!new File(PLUGIN_DIRECTORY).equals(jarFile.getParentFile()) && new File(PLUGIN_DIRECTORY,jarFile.getName()).isFile()) {
          urls = new URL[] { new File(PLUGIN_DIRECTORY, jarFile.getName()).toURI().toURL() };
          classLoader2 = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader());
        }
      } catch (MalformedURLException exc) {}
    } catch (MalformedURLException exc) {
      throw new TvBrowserException(getClass(), "error.1",
        "Loading Jar file of a plugin failed: {0}.",
        jarFile.getAbsolutePath(), exc);
    }

    // Get the plugin name
    String pluginName = jarFile.getName();
    if (pluginName.endsWith(".jar")) {
      pluginName = pluginName.substring(0, pluginName.length() - 4);
    }

    boolean isBlockedDataService = false;

    // Create a plugin instance
    try {
      Class pluginClass = classLoader.loadClass(pluginName.toLowerCase() + "." + pluginName);
      Method getVersion = pluginClass.getMethod("getVersion",new Class[0]);
      Version version1 = null;
      try {
        version1 = (Version)getVersion.invoke(pluginClass, new Object[0]);
      } catch (Exception e) {
      }
      if (version1 == null || version1.toString().equals("0.0.0.0")) {
        mLog.warning("Did not load plugin " + pluginName + ", version is too old.");
        return null;
      }

      if(pluginClass.getSuperclass().equals(devplugin.AbstractTvDataService.class) || classLoader2 != null) {
        getVersion = pluginClass.getMethod("getVersion",new Class[0]);
        version1 = (Version)getVersion.invoke(pluginClass, new Object[0]);

        if(pluginClass.getSuperclass().equals(devplugin.AbstractTvDataService.class)) {
          isBlockedDataService = Settings.propBlockedPluginArray.isBlocked(pluginName.toLowerCase() + "." + pluginName, version1);
        }
      }

      if(classLoader2 != null) {
        try {
          Class pluginClass2 = classLoader2.loadClass(pluginName.toLowerCase() + "." + pluginName);
          Method getVersion2 = pluginClass2.getMethod("getVersion",new Class[0]);

          Version version2 = (Version)getVersion2.invoke(pluginClass2, new Object[0]);

          if(version2.compareTo(version1) > 0) {
            return null;
          }
        }catch(Throwable t) {}
      }

      try {
        Method preInstancing = pluginClass.getMethod("preInstancing",new Class[0]);
        preInstancing.invoke(pluginClass,new Object[0]);
      }catch(Throwable ti) {}

      if(!isBlockedDataService) {
        plugin = pluginClass.newInstance();
      }
    }
    catch (Throwable thr) {
      throw new TvBrowserException(getClass(), "error.2",
         "Could not load plugin {0}.", jarFile.getAbsolutePath(), thr);
    }

    return plugin;
  }

  private Object loadBeanShellPlugin(File file) {
    return new BeanShellPluginProxy(file);
  }

  /**
   * Delete a Plugin
   * @param proxy Proxy for the Plugin that should be deleted
   * @return true if successful
   */
  public boolean deletePlugin(PluginProxy proxy) {
    if (deletePluginOrService(proxy)) {
      try {
          PluginProxyManager.getInstance().removePlugin(proxy);
      } catch (TvBrowserException exc) {
          ErrorHandler.handle(exc);
          return false;
      }
      return true;
    }
    return false;
  }

  /**
   * Delete a data service
   * @param service Data service that should be deleted
   * @return true if successful
   * @since 2.7
   */
  public boolean deleteDataService(TvDataServiceProxy service) {
    return deletePluginOrService(service);
  }

  private boolean deletePluginOrService(Object plugin) {
    // mark plugin file for deletion
    File file = mDeleteablePlugin.get(plugin);
    if (file != null) {
      Settings.propDeleteFilesAtStart.addItem(file.toString());

      // mark proxy file for deletion
      String proxyFile = getProxyFileName(file);
      Settings.propDeleteFilesAtStart.addItem(proxyFile);

      mDeleteablePlugin.remove(plugin);
      return true;
    }
    return false;
  }

  /**
   * Is a Plugin deleteable ?
   * @param plugin Plugin that should be deleted
   * @return true if deleteable
   */
  public boolean isPluginDeletable(PluginProxy plugin) {
    return mDeleteablePlugin.containsKey(plugin);
  }

  /**
   * Is a data service deleteable ?
   * @param service Data service that should be deleted
   * @return true if deleteable
   * @since 2.7
   */
  public boolean isDataServiceDeletable(TvDataServiceProxy service) {
    return mDeleteablePlugin.containsKey(service);
  }

  /**
   * delete all plugin proxies to force re-reading the plugin classes on next start
   */
  public void deleteAllPluginProxies() {
    File settingsDir = new File(Settings.getUserSettingsDirName());
    File[] proxyFiles = settingsDir.listFiles(new FilenameFilter() {
      public boolean accept(File dir, String name) {
        return name.endsWith(PLUGIN_PROXY_EXTENSION);
      }});
    if (proxyFiles != null) {
      for (File proxyFile : proxyFiles) {
        Settings.propDeleteFilesAtStart.addItem(proxyFile.toString());
      }
    }
  }
  
  /**
   * Gets the base infos for all available plugins.
   * <p>
   * @return The array with the base info for all available plugins.
   */
  public PluginBaseInfo[] getInfoOfAvailablePlugins() {
    ArrayList<PluginBaseInfo> availablePlugins = new ArrayList<PluginBaseInfo>();
    
    /* 1) get base info for plugins in user dir */
    getBaseInfoOfPluginsInDirectory(new File(Settings.propPluginsDirectory.getString()),availablePlugins);
    
    /* 2) get base info for plugins in the plugins folder */
    getBaseInfoOfPluginsInDirectory(new File(PluginProxyManager.PLUGIN_DIRECTORY),availablePlugins);

    /* 3) get base info for in the tvdataservice folder */
    getBaseInfoOfPluginsInDirectory(new File(TvDataServiceProxyManager.PLUGIN_DIRECTORY),availablePlugins);
    
    return availablePlugins.toArray(new PluginBaseInfo[availablePlugins.size()]);
  }
  
  private void getBaseInfoOfPluginsInDirectory(File directory, ArrayList<PluginBaseInfo> availablePlugins) {
    if (directory.exists()) {
      File[] pluginFiles = directory.listFiles(new FilenameFilter() {
        public boolean accept(File dir, String name) {
          for (String ignored : IGNORED_PLUGINS) {
            if (name.equalsIgnoreCase(ignored)) {
              return false;
            }
          }
          
          return name.toLowerCase().endsWith(".jar");
        }
      });
      
      getBaseInfoOfPlugins(pluginFiles,availablePlugins);
    }
  }
  
  private void getBaseInfoOfPlugins(File[] plugins, ArrayList<PluginBaseInfo> availablePlugins) {
    for(File plugin : plugins) {
      URL[] urls;
      
      /* Start of
       * Ugly workaround to prevent file lock of class loader on original plugin file */
      File temp = new File(System.getProperty("java.io.tmpdir"),"tvbTestPlugins");
      
      if(!temp.isDirectory()) {
        temp.mkdirs();
      }
      
      File pluginCopy = new File(temp,plugin.getName());
      
      try {
        if(pluginCopy.isFile()) {
          pluginCopy.delete();
        }
        
        IOUtilities.copy(plugin,pluginCopy);
        pluginCopy.deleteOnExit();
      } catch (IOException e1) {
        pluginCopy = plugin;
      }
      /* End of workaround */
      
      // Get the plugin name
      String pluginName = pluginCopy.getName();
      pluginName = pluginName.substring(0, pluginName.length() - 4);
      Class pluginClass = null;
      
      try {
        urls = new URL[] { pluginCopy.toURI().toURL() };
        ClassLoader classLoader = URLClassLoader.newInstance(urls, ClassLoader.getSystemClassLoader());
                        
        pluginClass = classLoader.loadClass(pluginName.toLowerCase() + "." + pluginName);
        Method getVersion = pluginClass.getMethod("getVersion",new Class[0]);
        
        try {
          Version version = (Version)getVersion.invoke(pluginClass, new Object[0]);
          
          PluginBaseInfo baseInfo = new PluginBaseInfo("java." + pluginClass.getName(),version);
          
          if(!availablePlugins.contains(baseInfo)) {
            availablePlugins.add(baseInfo);
          }
        } catch (Exception e) {
        }
        
        
      } catch (Throwable t) {
        urls = null;
        pluginClass = null;
        System.gc();
        
        PluginBaseInfo baseInfo = new PluginBaseInfo("java." + pluginName.toLowerCase() + "." + pluginName, new Version(0,0));
        
        if(!availablePlugins.contains(baseInfo)) {
          availablePlugins.add(baseInfo);
        }
        
        mLog.info("Could not load base info for plugin file '" + plugin.getAbsolutePath() + "'. Use default version instead.");
      }
    }
  }
  
  public boolean hasToShowMouseInfo() {
    return mShowMouseInfo;
  }

}
