/**
 * Created on Jun 18, 2005
 * $id:  $
 */
package org.jvnet.lookout.ui.core;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.jvnet.lookout.core.adapter.SearchAdapter;
import org.jvnet.lookout.core.model.SearchResultSource;
import org.jvnet.lookout.core.preference.Preferences;
import org.jvnet.lookout.core.util.Utilities;
import org.jvnet.lookout.ui.core.controller.SearchController;
import org.jvnet.lookout.ui.core.preference.Cleanable;
import org.jvnet.lookout.ui.core.tree.adapter.ModelToUIAdapter;
import org.jvnet.lookout.ui.core.tree.label.SearchResultsLabelProvider;
import org.osgi.framework.BundleContext;

/**
 * The main plugin class to be used in the desktop.
 */
public class Plugin extends AbstractUIPlugin implements ImageDescriptorProvider {

    // The shared instance.
    private static Plugin plugin;

    // Resource bundle.
    private ResourceBundle resourceBundle;

    private Map<String, ModelToUIAdapter> adapters;
    private Map<Integer, ModelToUIAdapter> sourceToAdapter;
    
    private Map<SearchResultSource, SearchResultsLabelProvider> sourceToLabelProvider;
    
    private org.jvnet.lookout.core.Plugin searchPlugin;
    
    private List<Cleanable> cleanables;

    private static final String PLUGIN_ID = Plugin.class.getPackage().getName();
    
    private static final String SEARCH_UI_PLUGIN_ID = "org.jvnet.lookout.ui.core";
    private static final String MODEL_TO_UI_ADAPTER_EXTENTION_POINT_ID = "adapter"; 
    
    private static final String ADAPTER_ELEMENT = "adapter"; 
    private static final String LABEL_PROVIDER_ELEMENT = "label-provider"; 
    
    private static final String CLASS_ATTRIBUTE = "class"; 
    private static final String ADAPTER_ATTRIBUTE = "adapter";     
    
    /**
     * The constructor.
     */
    public Plugin() {
        super();
        plugin = this;        
        cleanables = new ArrayList<Cleanable>(2);
    }
    
    /**
     * This method is called upon plug-in activation
     */
    public void start(BundleContext context) throws Exception {
        super.start(context);
        
        //Init the search plugin
        searchPlugin = org.jvnet.lookout.core.Plugin.getDefault();
        
        adapters = new HashMap<String, ModelToUIAdapter>(10);
        
        sourceToLabelProvider = new HashMap<SearchResultSource, SearchResultsLabelProvider>(5); 
        
        //Locate adapters and possible label providers
        locateAvailableExtensionParts();        
        
        //Register the adapters with the controller
        registerAdapters();
        
        indexAdaptersBySearchResultSource();
    }

    private void indexAdaptersBySearchResultSource() {
        
        sourceToAdapter = new HashMap<Integer, ModelToUIAdapter>(adapters.size());
        
        for (Iterator iter = adapters.keySet().iterator(); iter.hasNext();) {
            String adapterId = (String) iter.next();            
            ModelToUIAdapter modelToUIAdapter = adapters.get(adapterId);            
            sourceToAdapter.put(
                    searchPlugin.getSearchAdapter(adapterId).getSearchResultSource().getId(), 
                    modelToUIAdapter);
        }        
    }
    
    /**
     * Returns the SearchResultsLabelProvider associated with the 
     * provided SearchResultSource. If none is associated, it returns null.
     * @param source
     * @return
     */
    public SearchResultsLabelProvider getLabelProviderForSource(SearchResultSource source) {
        return sourceToLabelProvider.get(source); 
    }

    private void registerAdapters() {
        for (Iterator iter = adapters.keySet().iterator(); iter.hasNext();) {
            String adapterId = (String) iter.next();            
            ModelToUIAdapter modelToUIAdapter = adapters.get(adapterId);
            
            SearchAdapter adapter = searchPlugin.getSearchAdapter(adapterId);
            SearchController.registerSearchAdapter(adapter, modelToUIAdapter);
            
            //Adapters are actived by default, so need to check if it is actually active...
            boolean isActive = Utilities.containsInList
            (searchPlugin.getPluginPreferences().getString(Preferences.SearchableSearchAdapters),
            		adapterId, 
            		",");
            
            if(isActive) {
            	SearchController.activeSearchAdapter(adapter);
            }
        }
    }
    
    private void unregisterAdapters() {
        for (Iterator iter = adapters.keySet().iterator(); iter.hasNext();) {
            SearchController.unregisterSearchAdapter(searchPlugin.getSearchAdapter((String) iter.next()));
        }
    }
    
    private void locateAvailableExtensionParts() {
        
        IExtensionRegistry extensionRegistry = Platform.getExtensionRegistry();
        
        IExtensionPoint extensionPoint 
        = extensionRegistry.getExtensionPoint(SEARCH_UI_PLUGIN_ID, MODEL_TO_UI_ADAPTER_EXTENTION_POINT_ID);
        
        IExtension[] extensions = extensionPoint.getExtensions();
        
        locateAndProcessAdapters(extensions);
        
        locateAndProcessLabelProviders(extensions);
    }

    private void locateAndProcessAdapters(IExtension[] extensions) {
        
        for (int i = 0; i < extensions.length; i++) {
            IExtension extension = extensions[i];
            IConfigurationElement[] configurationElements = extension.getConfigurationElements();
            
            for (int j = 0; j < configurationElements.length; j++) {
                IConfigurationElement element = configurationElements[j];
                
                if(ADAPTER_ELEMENT.equals(element.getName())) {
                    
                    //Eagerly load these...
                    Object adapter = null;
                    String adapterId = null;
                    
                    try {
                        adapter = element.createExecutableExtension(CLASS_ATTRIBUTE);                        
                        adapterId = element.getAttribute(ADAPTER_ATTRIBUTE);                        
                    } catch (Exception e) {
                    	MultiStatus status = Utilities.createPluginStatus(
                    			Plugin.getDefault(),
                    			//CXB_EXTERNALIZE
                    			"Could not locate " + ModelToUIAdapter.class.getName() + ": '" + adapterId + "'",
                    			IStatus.WARNING,
                    			e
                    	);                	
                    	Plugin.getDefault().getLog().log(status);
                    	continue;
                    }
                    
                    if(adapter instanceof ModelToUIAdapter) {
                        adapters.put(adapterId, (ModelToUIAdapter)adapter);
                    } else {
                    	MultiStatus status = Utilities.createPluginStatus(
                    			Plugin.getDefault(),
                    			//CXB_EXTERNALIZE
                    			"Could not locate " + ModelToUIAdapter.class.getName()+ ": '" + adapterId + "'",
                    			IStatus.WARNING,
                    			new IllegalArgumentException
                    			//CXB_EXTERNALIZE
                    			("Class: '" + adapter.getClass().toString() + "' does not implement " + ModelToUIAdapter.class.toString())
                    	);                	
                    	Plugin.getDefault().getLog().log(status);                	
                    }                
                }
            }
        }
    }
    
    private void locateAndProcessLabelProviders(IExtension[] extensions) {
        
        for (int i = 0; i < extensions.length; i++) {
            IExtension extension = extensions[i];
            IConfigurationElement[] configurationElements = extension.getConfigurationElements();
            
            for (int j = 0; j < configurationElements.length; j++) {
                IConfigurationElement element = configurationElements[j];
                
                if(LABEL_PROVIDER_ELEMENT.equals(element.getName())) {
                    
                    //Eagerly load these...
                    Object labelProvider = null;
                    String adapterId = null;
                    
                    try {
                        labelProvider = element.createExecutableExtension(CLASS_ATTRIBUTE);
                        adapterId = element.getAttribute(ADAPTER_ATTRIBUTE);
                    } catch (Exception e) {
                    	MultiStatus status = Utilities.createPluginStatus(
                    			Plugin.getDefault(),
                    			//CXB_EXTERNALIZE
                    			"Could not locate " + SearchResultsLabelProvider.class.getName() + " for adapter with this ID: '" + adapterId + "'",
                    			IStatus.WARNING,
                    			e
                    	);                	
                    	Plugin.getDefault().getLog().log(status);
                        continue;
                    }
                    
                    if(labelProvider instanceof SearchResultsLabelProvider) {
                        SearchResultSource source = searchPlugin.getSearchAdapter(adapterId).getSearchResultSource();
                        sourceToLabelProvider.put(source, (SearchResultsLabelProvider)labelProvider);
                    } else {
                    	MultiStatus status = Utilities.createPluginStatus(
                    			Plugin.getDefault(),
                    			//CXB_EXTERNALIZE
                    			"Could not locate " + SearchResultsLabelProvider.class.getName()+ ": '" + adapterId + "'",
                    			IStatus.WARNING,
                    			new IllegalArgumentException
                    			//CXB_EXTERNALIZE
                    			("Class: '" + labelProvider.getClass().toString() + "' does not implement " + SearchResultsLabelProvider.class.toString())
                    	);                	
                    	
                    	Plugin.getDefault().getLog().log(status);                           
                    }                
                }       
            }            
        }
    }
    public void addCleanable(Cleanable cleanable) {
    	cleanables.add(cleanable);
    }

    /**
     * This method is called when the plug-in is stopped
     */
    public void stop(BundleContext context) throws Exception {
        
        //Unregister...
        unregisterAdapters();

    	//Need to give a chance to any cleanable to cleanup.
    	for (Iterator iter = cleanables.iterator(); iter.hasNext();) {
			((Cleanable) iter.next()).clean();			
		}

        //Clear references...
        searchPlugin = null;
        adapters = null;
        plugin = null;
        resourceBundle = null;
    
        //Cleanup the Utilities
        org.jvnet.lookout.ui.core.util.Utilities.cleanup();
        
        //Super stop...
        super.stop(context);
    }

    /**
     * Returns the shared instance.
     */
    public static Plugin getDefault() {
        return plugin;
    }

    /**
     * Returns the string from the plugin's resource bundle, or 'key' if not
     * found.
     */
    public static String getResourceString(String key) {
        ResourceBundle bundle = Plugin.getDefault().getResourceBundle();
        try {
            return (bundle != null) ? bundle.getString(key) : key;
        } catch (MissingResourceException e) {
            return key;
        }
    }

    /**
     * Returns the plugin's resource bundle,
     */
    public ResourceBundle getResourceBundle() {
        try {
            if (resourceBundle == null)
                resourceBundle = ResourceBundle.getBundle(PLUGIN_ID + ".PluginResources");
        } catch (MissingResourceException x) {
            resourceBundle = null;
        }
        return resourceBundle;
    }

    /**
     * Returns an image descriptor for the image file at the given plug-in
     * relative path.
     * 
     * @param path the path
     * @return the image descriptor
     */
    public ImageDescriptor getImageDescriptor(String path) {
        return AbstractUIPlugin.imageDescriptorFromPlugin(PLUGIN_ID,path);
    }
}
