/**
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * Copyright © 2009 Mindoo GmbH
 *
 * @author Tammo Riedinger
 *
 */
package com.mindoo.ec09.example3;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

import org.eclipse.albireo.core.SwingControl;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.part.ViewPart;

import com.mindoo.ec09.framework.actions.ActionManager;
import com.mindoo.ec09.legacy.AbstractLegacyApplication;

/**
 * This sample demonstrates a simple life-cycle of a Swing application being integrated as a single view
 * into the Lotus Notes client.<br>
 * Additionally in this sample, statically defined action-sets are shown/hidden according to some internal state
 * of the sample application.
 *  
 * @author Tammo Riedinger
 */
public class SampleView extends ViewPart {
	/**
	 * Member to hold a reference to the displayed control.
	 */
	private SwingControl control;
	/**
	 * Member to hold a reference to the application being generated.
	 * This reference is used to free any resources used by the application
	 * when the ViewPart is disposed.
	 * In this case, the application-reference can be regarded as a bridge to the running application.
	 */
	private AbstractLegacyApplication app;
	private BufferingActionSetManager m_actionSetManager;

	@Override
	public void createPartControl(final Composite parent) {
		// instantiate the manager for the actionsets that are to be displayed
		m_actionSetManager=new BufferingActionSetManager(this);

		// When the Lotus Notes client is shut-down and the SampleView is active,
		// the visible actionsets are not hidden properly.
		// After a restart, the client will restore these sets, which might result in
		// visible sets that should not actually be displayed.
		// As a workaround, we can hide all sets that are declared for this sample.
		// (the global-set is deliberately spared out here)
		m_actionSetManager.hideActionSets(new String[] {"com.mindoo.ec09.legacy.PictureView","com.mindoo.ec09.legacy.ChartView","com.mindoo.ec09.legacy.TextView"});
		
		// start of life-cycle
		// Initialize the application and reposition its main-component	
		// to the SwingControl (e.g. return the main-component in SwingControl#createSwingComponent()).
		control=new SwingControl(parent, SWT.NONE) {
			@Override
			protected JComponent createSwingComponent() {
				try {
					// initialize our legacy application while overriding some of its methods
					app=new AbstractLegacyApplication() {
						@Override
					    protected void installMenuBar(Component window, Container contentPane) {
							// do nothing
						}
						
						@Override
						protected void installContentPane(Component window, Container contentPane) {
							super.installContentPane(window, contentPane);
							
							if (window instanceof JPanel) {
								((JPanel)window).add(contentPane,BorderLayout.CENTER);
							}
							else
								throw new RuntimeException("Unknown type of application window");
						}

					    @Override
					    protected Component createWindow() {
					    	JPanel panel=new JPanel(new BorderLayout());
					    	
							return panel;
						}
					    
					    @Override
						protected void show(Component window) {
							// do nothing
						}

						@Override
						protected void hide(Component window) {
							// do nothing
						}
						
						@Override
						protected ActionManager createActionManager(Component window, Container contentPane) {
							return new ActionManager(null,null) {
								@Override
								public void registerAction(Action a, String setID, String menuName, String toolbarName) {
									if (EXIT_ACTION==a) // do not append the exit-action
										return;
									super.registerAction(a, setID, menuName, toolbarName);
								}
								
								@Override
								public void activate(final String... setIDs) {
									super.activate(setIDs);
									
									// report activation of actionsets
									Display.getDefault().asyncExec(new Runnable() {
										public void run() {
											m_actionSetManager.activate(toExternalIDs(setIDs));
										}
									});
								}
								
								@Override
								public void deActivate(final String... setIDs) {
									super.deActivate(setIDs);
									// report deactivation of actionsets
									Display.getDefault().asyncExec(new Runnable() {
										public void run() {
											m_actionSetManager.deActivate(toExternalIDs(setIDs));
										}
									});
								}
								
								/**
								 * Convert all internal ids to those that do not conflict with other plugins.<br>
								 * We achieve this by prepending the id of the plugin.
								 * 
								 * @param ids		the ids
								 * @return			the converted ids
								 */
								private String[] toExternalIDs(String... ids) {
									ArrayList<String> externalIDs=new ArrayList<String>(ids.length);
									
									for (int i=0;i<ids.length;i++)
										externalIDs.add(Activator.PLUGIN_ID + ":" + ids[i]);
									
									return externalIDs.toArray(new String[0]);
								}
							};
						};
					};

					app.launch();
				} catch (Throwable t) {
					throw new RuntimeException(t);
				}
				

				// return the content-pane of the applet to be displayed inside the Swing-Control
				return (JComponent)((app!=null) ? app.getWindow() : null);
			}
			
			@Override
		    protected void afterComponentCreatedSWTThread() {
		    	// eventually make sure the visible frame has the focus
		    }

			@Override
			public Composite getLayoutAncestor() {
				return parent;
			}
		};
	}

	@Override
	public void dispose() {
		if (m_actionSetManager!=null)
			m_actionSetManager.dispose();
		
		// shut-down the instance of your legacy-application, 
		// when the last related ViewPart
		// in this case we use one instance per ViewPart
		super.dispose();

		if (app!=null) {
			// shut-down the application
			app.exit();
		}
		// end of life-cycle
	}

	@Override
	public void setFocus() {
		// hand the focus over to the control, whenever the ViewPart receives the focus
		if (control!=null && !control.isDisposed())
			control.setFocus();
	}

	/**
	 * Method to find a Swing action with the given name for the current view.<br>
	 * Note: Call this method from the Swing-EDT.
	 * 
	 * @param name			name of the action
	 * @return				the action or <code>null</code>
	 */
	public Action swing_findAction(String name) {
		if (app!=null) {
			ActionManager am=app.getActionManager();
			for (String setID:am.getActiveSetIDs()) {
				Iterator<Action> actions=am.getActions(setID);
				
				if (actions!=null) {
					while (actions.hasNext()) {
						Action a=actions.next();
						
						if (name.equals(a.getValue(Action.NAME)))
							return a;
					}
				}
			}
		}
		
		return null;
	}
	
	/**
	 * Method to be called in order to run a Swing-action with the given name.<br>
	 * The action will only be run, if it is currently active.
	 * 
	 * @param name		the name of the action
	 */
	public void runSwingAction(final String name) {
		if (app!=null) {
			SwingUtilities.invokeLater(new Runnable() {
				public void run() {
					Action a=swing_findAction(name);
					if (a!=null && a.isEnabled()) {
						Object command=a.getValue(Action.ACTION_COMMAND_KEY);
						
						a.actionPerformed(new ActionEvent(app.getWindow(),0,(command!=null) ? command.toString() : ""));
					}
				}
			});
		}
	}
	
	/**
	 * ActionSetManager implementation that is buffering the actionsets that are currently being displayed.<br>
	 * Use {@link #activate(String[])} and {@link #deActivate(String[])} respectively to show or hide actionsets.
	 * 
	 * @author Tammo Riedinger
	 */
	private static class BufferingActionSetManager extends ActionSetManager {
		/** map of active action sets */
		private HashMap<String,String> m_actionSetIds=new HashMap<String,String>();

		/**
		 * Creates an instance of this class.
		 * 
		 * @param part		part whose activation/deactivation is being monitored
		 */
		public BufferingActionSetManager(IWorkbenchPart part) {
			super(part);
		}

		
		@Override
		protected String[] getActiveActionSetIDs() {
			return m_actionSetIds.keySet().toArray(new String[0]);
		}
		
		/**
		 * Show the given actionsets.
		 * 
		 * @param actionSetIDs		ids of actionsets to be activated
		 */
		public void activate(String[] actionSetIDs) {
			for (String id:actionSetIDs)
				m_actionSetIds.put(id,id);
			
			update();
		}
		
		/**
		 * Hide the given actionsets.
		 * 
		 * @param actionSetIDs		ids of actionsets to be hidden
		 */
		public void deActivate(String[] actionSetIDs) {
			for (String id:actionSetIDs)
				m_actionSetIds.remove(id);
			
			update();
		}
	}
}