/*******************************************************************************
 * Copyright (c) 2007 Wind River Systems, Inc. and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Markus Schorn - initial API and implementation
 *******************************************************************************/ 

package org.eclipse.cdt.internal.ui.viewsupport;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.ui.IPartListener2;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.contexts.IContextActivation;
import org.eclipse.ui.contexts.IContextService;
import org.eclipse.ui.navigator.CommonNavigator;
import org.eclipse.ui.part.IPage;
import org.eclipse.ui.views.contentoutline.ContentOutline;

import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.ui.CUIPlugin;

import org.eclipse.cdt.internal.ui.editor.CContentOutlinePage;

/**
 * Once registered as windows listener, observes all part activations. Whenever the
 * CDT outline view is brought to the front, the CDT context is set.
 */
public class CDTContextActivator implements IWindowListener, IPartListener2 {
	private static CDTContextActivator sInstance= new CDTContextActivator();
	
	private Map<ContentOutline, IContextActivation> fActivationPerOutline = new HashMap<ContentOutline, IContextActivation>();
	private Map<CommonNavigator, SelectionListener> fActivationPerNavigator= new HashMap<CommonNavigator, SelectionListener>();
	private Collection<IWorkbenchWindow> fWindows= new HashSet<IWorkbenchWindow>();

	
	private CDTContextActivator() {
	}
	
	public static CDTContextActivator getInstance() {
		return sInstance;
	}
	
	public void install() {
		IWorkbench workbench = PlatformUI.getWorkbench();
		if (workbench != null) {
			// listen for new windows
			workbench.addWindowListener(this);
			IWorkbenchWindow[] wnds= workbench.getWorkbenchWindows();
			for (int i = 0; i < wnds.length; i++) {
				IWorkbenchWindow window = wnds[i];
				register(window);
			}
			// register open windows
			IWorkbenchWindow ww= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
			if (ww != null) {
				IWorkbenchPage activePage = ww.getActivePage();
				if (activePage != null) {
					IWorkbenchPartReference part= activePage.getActivePartReference();
					if (part != null) {
						partActivated(part);
					}
				}
			}
		}
	}

	public void uninstall() {
		for (Iterator<IWorkbenchWindow> iterator = fWindows.iterator(); iterator.hasNext();) {
			IWorkbenchWindow window = iterator.next();
			unregister(window);
		}
		for (Iterator<SelectionListener> iterator = fActivationPerNavigator.values().iterator(); iterator.hasNext();) {
			SelectionListener l= iterator.next();
			l.uninstall();
		}
	}

	private void register(IWorkbenchWindow wnd) {
		wnd.getPartService().addPartListener(this);
		fWindows.add(wnd);
	}
	
	private void unregister(IWorkbenchWindow wnd) {
		wnd.getPartService().removePartListener(this);
		fWindows.remove(wnd);
	}

	public void windowOpened(IWorkbenchWindow window) {
		register(window);
	}

	public void windowClosed(IWorkbenchWindow window) {
		unregister(window);
	}

	public void windowActivated(IWorkbenchWindow window) {
	}
	
	public void windowDeactivated(IWorkbenchWindow window) {
	}

	private void onContentOutlineClosed(ContentOutline outline) {
		fActivationPerOutline.remove(outline);
	}
	
	private void onContentOutlineActivated(ContentOutline outline) {
		IPage page = outline.getCurrentPage();
		if (page instanceof CContentOutlinePage) {
			if (!fActivationPerOutline.containsKey(outline)){
				// cdt outline activated for the first time
				IContextService ctxtService = (IContextService)outline.getViewSite().getService(IContextService.class);
				IContextActivation activateContext = ctxtService.activateContext(CUIPlugin.CVIEWS_SCOPE);
				fActivationPerOutline.put(outline,activateContext);
			}
		} 
		else {
			IContextActivation activation = fActivationPerOutline.remove(outline); 
			if (activation != null) {
				// other outline page brought to front
				IContextService ctxtService = (IContextService)outline.getViewSite().getService(IContextService.class);
				ctxtService.deactivateContext(activation);
			}
		}
	}

	private static class SelectionListener implements ISelectionChangedListener {
		private IWorkbenchPartSite fSite;
		private IContextService fCtxService;
		private IContextActivation fActivation;

		public SelectionListener(IWorkbenchPartSite site) {
			fSite= site;
			fCtxService= (IContextService)fSite.getService(IContextService.class);
			ISelectionProvider sp= site.getSelectionProvider();
			
			if (sp != null && fCtxService != null) {
				sp.addSelectionChangedListener(this);
				onNewSelection(sp.getSelection());
			}
		}

		public void uninstall() {
			ISelectionProvider sp= fSite.getSelectionProvider();
			if (sp != null && fCtxService != null) {
				onNewSelection(null);
				sp.removeSelectionChangedListener(this);
			}
		}
		
		public void selectionChanged(SelectionChangedEvent event) {
			onNewSelection(event.getSelection());
		}

		private void onNewSelection(ISelection selection) {
			boolean isRelevant= false;
			if (selection instanceof IStructuredSelection && !selection.isEmpty()) {
				if (((IStructuredSelection) selection).getFirstElement() instanceof ICElement) {
					isRelevant = true;
				}
			}
			if (isRelevant) {
				if (fActivation == null) {
					fActivation= fCtxService.activateContext(CUIPlugin.CVIEWS_SCOPE);
				}
			}
			else {
				if (fActivation != null) {
					fCtxService.deactivateContext(fActivation);
					fActivation= null;
				}
			}
		}
	}

	private void onCommonNavigatorActivated(CommonNavigator part) {
		SelectionListener l= fActivationPerNavigator.get(part);
		if (l == null) {
			l= new SelectionListener(part.getSite());
			fActivationPerNavigator.put(part, l);
		}
	}

	private void onCommonNavigatorClosed(CommonNavigator part) {
		fActivationPerNavigator.remove(part);
	}

	public void partActivated(IWorkbenchPartReference partRef) {
		IWorkbenchPart part= partRef.getPart(false);
		if (part instanceof ContentOutline) {
			onContentOutlineActivated((ContentOutline) part);
		}
		else if (part instanceof CommonNavigator) {
			onCommonNavigatorActivated((CommonNavigator) part);
		}
	}

	public void partClosed(IWorkbenchPartReference partRef) {
		IWorkbenchPart part= partRef.getPart(false);
		if (part instanceof ContentOutline) {
			onContentOutlineClosed((ContentOutline)part);
		}
		else if (part instanceof CommonNavigator) {
			onCommonNavigatorClosed((CommonNavigator) part);
		}
	}

	public void partBroughtToTop(IWorkbenchPartReference partRef) {
	}

	public void partDeactivated(IWorkbenchPartReference partRef) {
	}

	public void partOpened(IWorkbenchPartReference partRef) {
	}

	public void partHidden(IWorkbenchPartReference partRef) {
	}

	public void partVisible(IWorkbenchPartReference partRef) {
	}

	public void partInputChanged(IWorkbenchPartReference partRef) {
	}
}
