/*******************************************************************************
 * Copyright (c) 2006, 2012 QNX Software Systems and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    QNX - Initial API and implementation
 *    Markus Schorn (Wind River Systems)
 *******************************************************************************/

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

import org.eclipse.cdt.core.CCorePlugin;
import org.eclipse.cdt.core.index.IIndex;
import org.eclipse.cdt.core.index.IIndexBinding;
import org.eclipse.cdt.core.index.IIndexManager;
import org.eclipse.cdt.core.index.IIndexName;
import org.eclipse.cdt.core.index.IndexLocationFactory;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.CoreModel;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.internal.core.CCoreInternals;
import org.eclipse.cdt.internal.ui.util.EditorUtility;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.texteditor.ITextEditor;

/**
 * @author Doug Schaefer
 *
 */
public class OpenDefinitionAction extends IndexAction {

	public OpenDefinitionAction(IndexView view, TreeViewer viewer) {
		super(view, viewer, CUIPlugin.getResourceString("IndexView.openDefinition.name"));//$NON-NLS-1$
	}

	private IndexNode getBindingNode() {
		ISelection selection = viewer.getSelection();
		if (!(selection instanceof IStructuredSelection))
			return null;
		Object[] objs = ((IStructuredSelection) selection).toArray();
		if (objs.length == 1 && objs[0] instanceof IndexNode) {
			IndexNode node = (IndexNode) objs[0];
			if (node.fObject instanceof IIndexBinding) {
				return node;
			}
		}
		return null;
	}

	@Override
	public void run() {
		IndexNode bindingNode = getBindingNode();
		if (bindingNode == null) {
			return;
		}
		try {
			ICProject cproject = bindingNode.getProject();
			if (cproject != null) {
				IIndex index = CCorePlugin.getIndexManager().getIndex(cproject);
				if (!openDefinition(cproject, bindingNode, index)) {
					index = CCorePlugin.getIndexManager().getIndex(CoreModel.getDefault().getCModel().getCProjects(),
							IIndexManager.ADD_EXTENSION_FRAGMENTS_NAVIGATION);
					openDefinition(cproject, bindingNode, index);
				}
			}
		} catch (CoreException e) {
			CUIPlugin.log(e);
		} catch (InterruptedException e) {
		}
	}

	private boolean openDefinition(ICProject cproject, IndexNode bindingNode, IIndex index)
			throws InterruptedException, CoreException, CModelException, PartInitException {
		index.acquireReadLock();
		try {
			if (indexView.getLastWriteAccess(cproject) != CCoreInternals.getPDOMManager().getPDOM(cproject)
					.getLastWriteAccess()) {
				return true;
			}
			IIndexName[] defs = index.findDefinitions((IIndexBinding) bindingNode.fObject);
			if (defs.length > 0) {
				showInEditor(defs[0]);
				return true;
			}
			defs = index.findDeclarations((IIndexBinding) bindingNode.fObject);
			if (defs.length > 0) {
				showInEditor(defs[0]);
				return true;
			}
		} finally {
			index.releaseReadLock();
		}
		return false;
	}

	private void showInEditor(IIndexName name) throws CModelException, PartInitException, CoreException {
		IPath path = IndexLocationFactory.getPath(name.getFile().getLocation());
		if (path != null) {
			IEditorPart editor = EditorUtility.openInEditor(path, null);
			if (editor != null && editor instanceof ITextEditor) {
				ITextEditor textEditor = (ITextEditor) editor;
				int nodeOffset = name.getNodeOffset();
				int nodeLength = name.getNodeLength();
				try {
					if (nodeLength == -1) {
						// This means the offset is actually a line number
						IDocument document = textEditor.getDocumentProvider().getDocument(editor.getEditorInput());
						nodeOffset = document.getLineOffset(nodeOffset);
						nodeLength = document.getLineLength(nodeOffset);
					}
					textEditor.selectAndReveal(nodeOffset, nodeLength);
				} catch (BadLocationException e) {
					CUIPlugin.log(e);
				}
			}
		}
	}

	@Override
	public boolean valid() {
		return getBindingNode() != null;
	}
}
