/*******************************************************************************
 * Copyright (c) 2008 Symbian Software Systems 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:
 * Andrew Ferguson (Symbian) - Initial implementation
 *******************************************************************************/
package org.eclipse.cdt.internal.ui.text.doctools;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.progress.WorkbenchJob;

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.text.doctools.IDocCommentOwner;
import org.eclipse.cdt.ui.text.doctools.IDocCommentOwnershipListener;

import org.eclipse.cdt.internal.ui.editor.ITranslationUnitEditorInput;
import org.eclipse.cdt.internal.ui.util.EditorUtility;
import org.eclipse.cdt.internal.ui.util.ExternalEditorInput;

/**
 * Listens to change in doc-comment ownership and reinitializes
 * the editors (or a safe superset of) that need reopening.
 */
public class EditorReopener implements IDocCommentOwnershipListener {

	/*
	 * @see org.eclipse.cdt.ui.text.doctools.IDocCommentOwnershipListener#ownershipChanged(org.eclipse.core.resources.IResource, boolean, org.eclipse.cdt.ui.text.doctools.IDocCommentOwner, org.eclipse.cdt.ui.text.doctools.IDocCommentOwner)
	 */
	public void ownershipChanged(IResource resource, boolean recursive,
			IDocCommentOwner old, IDocCommentOwner newOwner) {
		IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if(window!=null) {
			try {
				IEditorPart[] parts= getEditorsToRepon(window, resource);
				if(queryIfNeeded(window.getShell(), parts)) {
					reopenEditors(window, parts);
				}
			} catch(CoreException ce) {
				CCorePlugin.log(ce);
			}
		}
	}

	/*
	 * @see org.eclipse.cdt.ui.text.doctools.IDocCommentOwnershipListener#workspaceOwnershipChanged(org.eclipse.cdt.ui.text.doctools.IDocCommentOwner, org.eclipse.cdt.ui.text.doctools.IDocCommentOwner)
	 */
	public void workspaceOwnershipChanged(IDocCommentOwner old, IDocCommentOwner newOwner) {
		IWorkbenchWindow window= PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		if(window!=null) {
			try {
				IEditorPart[] parts= getEditorsToRepon(window, null);
				if(queryIfNeeded(window.getShell(), parts)) {
					reopenEditors(window, parts);
				}
			} catch(CoreException ce) {
				CCorePlugin.log(ce);
			}
		}
	}

	/**
	 * @param window
	 * @param resource may be null to indicate all CDT editors should be reopened
	 * @return an array of {@link IEditorPart} objects that might need to be reinitialized
	 * based on doc-comment ownership of the specified resource changing
	 */
	/*
	 * This could be smarter in determining smaller sets of editors to re-open
	 */
	private IEditorPart[] getEditorsToRepon(IWorkbenchWindow window, IResource resource) {
		List<IEditorPart> needReopening= new ArrayList<IEditorPart>();
		if(window.getActivePage()!=null) {
			IEditorReference[] es= window.getActivePage().getEditorReferences();
			for(int i=0; i<es.length; i++) {
				IEditorPart part= es[i].getEditor(false);
				if(part!=null) {
					IEditorInput iei= part.getEditorInput();
					if(resource!=null) {
						if(iei instanceof IFileEditorInput) {
							IFile file= ((IFileEditorInput) iei).getFile();
							IProject project= resource.getProject();
							if(file.getProject().equals(project) && CoreModel.hasCNature(project)) {
								needReopening.add(part);
							}
						}
					} else {
						if(iei instanceof ITranslationUnitEditorInput || iei instanceof IFileEditorInput) {
							needReopening.add(part);
						}
					}
				}
			}
		}
		return needReopening.toArray(new IEditorPart[needReopening.size()]);
	}

	private boolean queryIfNeeded(Shell shell, IEditorPart[] editorParts) throws CoreException {
		boolean anyUnsaved= false;
		for(int j=0; j<editorParts.length; j++)
			if(editorParts[j].isSaveOnCloseNeeded())
				anyUnsaved= true;

		boolean saveAndReopen= !anyUnsaved;
		if(anyUnsaved) {
			String title= Messages.EditorReopener_ShouldSave_Title;
			String msg= Messages.EditorReopener_ShouldSave_Message; 

			if (MessageDialog.openQuestion(shell, title, msg))
				saveAndReopen= true;
		}
		return saveAndReopen;
	}

	private void reopenEditors(final IWorkbenchWindow window, final IEditorPart[] editorParts) throws CoreException {
		WorkbenchJob job= new WorkbenchJob(Messages.EditorReopener_ReopenJobStart) {
			public IStatus runInUIThread(IProgressMonitor monitor) {
				IEditorPart oldActive= window.getActivePage().getActiveEditor();
				IEditorPart newActive= null;

				for(int j=0; j<editorParts.length; j++) {
					IEditorPart oldPart= editorParts[j];
					oldPart.doSave(new NullProgressMonitor());
					window.getActivePage().closeEditor(oldPart, false);
					IEditorInput oldInput= oldPart.getEditorInput();

					try {
						IEditorPart newPart= null;
						if(oldInput instanceof IFileEditorInput) {
							newPart= EditorUtility.openInEditor(((IFileEditorInput)oldInput).getFile());
						} else if(oldInput instanceof ExternalEditorInput) {
							ExternalEditorInput eei= (ExternalEditorInput) oldInput;
							ICElement element= CoreModel.getDefault().create(eei.getMarkerResource());
							newPart= EditorUtility.openInEditor(eei.getPath(null), element);
						}
						if(oldPart == oldActive)
							newActive= newPart;
					} catch(PartInitException pie) {
						CCorePlugin.log(pie);
					} catch(CModelException cme) {
						CCorePlugin.log(cme);
					}
				}

				if(newActive != null) {
					window.getActivePage().activate(newActive);
				}
				return new Status(IStatus.OK, CUIPlugin.PLUGIN_ID, Messages.EditorReopener_ReopenJobComplete);
			}
		};
		job.schedule();
	}
}
