package org.micromanager.pluginmanagement;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JMenu;
import javax.swing.JMenuBar;

import mmcorej.TaggedImage;

import org.micromanager.api.DataProcessor;
import org.micromanager.api.MMPlugin;
import org.micromanager.api.MMProcessorPlugin;
import org.micromanager.MMStudio;
import org.micromanager.utils.GUIUtils;
import org.micromanager.utils.ReportingUtils;

/*
 * This class is responsible for general plugin management.
 */
public class PluginManager {
   private JMenu pluginMenu_;
   private Map<String, JMenu> pluginSubMenus_;
   private PluginLoader pluginLoader_;

   private MMStudio studio_;
   private JMenuBar menuBar_;

   public PluginManager(MMStudio studio, JMenuBar menuBar) {
      studio_ = studio;
      menuBar_ = menuBar;
      pluginLoader_ = new PluginLoader(this);
   }

   public Thread initializePlugins() {
      pluginMenu_ = GUIUtils.createMenuInMenuBar(menuBar_, "Plugins");
      GUIUtils.addMenuItem(pluginMenu_, "Image Processors...",
            "Display the image processing pipeline",
            new Runnable() {
               public void run() {
                  studio_.showPipelinePanel();
               }
            });
      pluginMenu_.addSeparator();

      Thread loadThread = new Thread(new ThreadGroup("Plugin loading"),
            new Runnable() {
               @Override
               public void run() {
                  pluginLoader_.loadPlugins();
               }
      });
      // Needed for loading clojure-based jars:
      loadThread.setContextClassLoader(getClass().getClassLoader());
      loadThread.start();
      return loadThread;
   }

   /**
    * Adds plugin items to the plugins menu
    * Adds submenus (currently only 1 level deep)
    * @param plugin - plugin to be added to the menu
    */
   public void addPluginToMenu(final PluginItem plugin) {
      List<String> path = plugin.getMenuPath();
      final PluginManager thisInstance = this;
      if (path.size() == 1) {
         GUIUtils.addMenuItem(pluginMenu_, plugin.getMenuItem(), plugin.getTooltip(),
                 new Runnable() {
            public void run() {
               thisInstance.displayPlugin(plugin);
            }
         });
      }
      if (path.size() == 2) {
         if (pluginSubMenus_ == null) {
            pluginSubMenus_ = new HashMap<String, JMenu>();
         }
         String groupName = path.get(0);
         JMenu submenu = pluginSubMenus_.get(groupName);
         if (submenu == null) {
            submenu = new JMenu(groupName);
            pluginSubMenus_.put(groupName, submenu);
            submenu.validate();
            pluginMenu_.add(submenu);
         }
         GUIUtils.addMenuItem(submenu, plugin.getMenuItem(), plugin.getTooltip(),
                 new Runnable() {
            public void run() {
               thisInstance.displayPlugin(plugin);
            }
         });
      }
      
      pluginMenu_.validate();
      menuBar_.validate();
   }

   // Handle a plugin being selected from the Plugins menu.
   private void displayPlugin(final PluginItem plugin) {
      ReportingUtils.logMessage("Plugin command: " + plugin.getMenuItem());
      plugin.instantiate();
      switch (plugin.getPluginType()) {
         case PLUGIN_STANDARD:
            // Standard plugin; create its UI.
            ((MMPlugin) plugin.getPlugin()).show();
            break;
         case PLUGIN_PROCESSOR:
            // Processor plugin; check for existing processor of 
            // this type and show its UI if applicable; otherwise
            // create a new one.
            // First show the pipeline GUI.
            studio_.showPipelinePanel();
            MMProcessorPlugin procPlugin = (MMProcessorPlugin) plugin.getPlugin();
            String procName = PluginLoader.getNameForPluginClass(procPlugin.getClass());
            DataProcessor<TaggedImage> pipelineProcessor = studio_.getAcquisitionEngine().getProcessorRegisteredAs(procName);
            if (pipelineProcessor == null) {
               // No extant processor of this type; make a new one,
               // which automatically adds it to the pipeline.
               pipelineProcessor = studio_.getAcquisitionEngine().makeProcessor(procName, studio_);
            }
            if (pipelineProcessor != null) {
               // Show the GUI for this processor. The extra null check is 
               // because making the processor (above) could have failed.
               pipelineProcessor.makeConfigurationGUI();
            }
            break;
         default:
            // Unrecognized plugin type; just skip it. 
            ReportingUtils.logError("Unrecognized plugin type " + plugin.getPluginType());
      }
   }

   public void disposePlugins() {
      pluginLoader_.disposePlugins();
   }   
}

@@ -580,16 +580,22 @@
 private 
+final 
 Map%3CStri
@@ -620,16 +620,47 @@
 ubMenus_
+ = new HashMap%3CString, JMenu%3E()
 ;%0A   pri
@@ -664,16 +664,22 @@
 private 
+final 
 PluginLo
@@ -1035,302 +1035,8 @@
 s%22);
-%0A      GUIUtils.addMenuItem(pluginMenu_, %22Image Processors...%22,%0A            %22Display the image processing pipeline%22,%0A            new Runnable() %7B%0A               public void run() %7B%0A                  studio_.showPipelinePanel();%0A               %7D%0A            %7D);%0A      pluginMenu_.addSeparator();
 %0A%0A  
@@ -1785,16 +1785,48 @@
  == 1) %7B
+ // Add plugin to top-level menu
 %0A       
@@ -2058,24 +2058,29 @@
     %7D%0A      
+else 
 if (path.siz
@@ -2094,119 +2094,33 @@
 2) %7B
-%0A         if (pluginSubMenus_ == null) %7B%0A            pluginSubMenus_ = new HashMap%3CString, JMenu%3E();%0A         %7D
+ // Add plugin to submenu
 %0A   
@@ -2405,32 +2405,262 @@
 _.add(submenu);%0A
+            if (groupName.equals(%22Image Processing%22)) %7B%0A               // Sorry for this hard-coded special behavior%0A               addImageProcessorConfigureMenuItem(submenu);%0A               submenu.addSeparator();%0A            %7D%0A
          %7D%0A     
@@ -2898,63 +2898,448 @@
     
-%0A      pluginMenu_.validate();%0A      menuBar_.validate(
+else %7B%0A         // Ignore plugins in deeper paths.%0A      %7D%0A%0A      pluginMenu_.validate();%0A      menuBar_.validate();%0A   %7D%0A%0A   private void addImageProcessorConfigureMenuItem(JMenu menu) %7B%0A      GUIUtils.addMenuItem(menu, %22Configure Processors...%22,%0A            %22Set up the image processor pipeline%22,%0A            new Runnable() %7B%0A               public void run() %7B%0A                  studio_.showPipelinePanel();%0A               %7D%0A            %7D
 );%0A 

Move pipeline GUI menu item into submenu