/* 
-*- coding: latin-1 -*-

This file is part of RefactorErl.

RefactorErl is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

RefactorErl is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with RefactorErl.  If not, see <http://plc.inf.elte.hu/erlang/>.

The Original Code is RefactorErl.

The Initial Developer of the Original Code is Eötvös Loránd University.
Portions created  by Eötvös Loránd University and ELTE-Soft Ltd.
are Copyright 2007-2025 Eötvös Loránd University, ELTE-Soft Ltd.
and Ericsson Hungary. All Rights Reserved.
*/

package com.refactorerl.ui.presentation.codebrowser;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextOperationTarget;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeNode;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.progress.UIJob;
import org.eclipse.ui.texteditor.ITextEditor;

import com.refactorerl.ui.logic.queries.QueryResultElement;
import com.refactorerl.ui.presentation.Activator;
import com.refactorerl.ui.presentation.filelist.FileListContentProvider;

/**
 * This handler will open a new editor part on a file which is present on the
 * RefactorErl server: and Erlang editor, if Erlide plugin is present,
 * org.eclipse.ui.DefaultTextEditor otherwise.
 * 
 * The IEditorInput of the opened editor will be a FileEditorInput, if the
 * supplied file is located in the Workspace and is added to RefactorErl. It
 * will be a RemoteTemporaryFileEditorInput if the is added to RefactorErl, but
 * not located in the Workspace.
 * 
 * If a file path string is passed as a command parameter
 * (com.refactorerl.ui.showFileCommand.path) it will use this file. It's
 * important to note that for files originally located at a remote RefactorErl
 * server, the remote file path should be passed. For these files
 * RemoteTemporaryFileEditorInput instances will be created, which will create
 * local copies of these remote files.
 * 
 * Optionally an integer string can be passed in the
 * com.refactorerl.ui.showFileCommand.cursorpos parameter to mark a position
 * where the editor should jump after opening.
 * 
 * If no command parameter is supplied it will parse the currently active
 * selection for a file path.
 * 
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
public class ShowFileHandler extends AbstractHandler {

	// TODO these shouldn't be global
	private int start = -1; // will hold the start-end positions of the last
							// selected file
	private int end = -1;

	@Override
	public Object execute(ExecutionEvent event) throws ExecutionException {

		IWorkbenchPage activePage = HandlerUtil.getActiveWorkbenchWindow(event).getActivePage();

		Shell shell = activePage.getActivePart().getSite().getShell();

		String pathParam = event.getParameter("com.refactorerl.ui.showFileCommand.path"); //$NON-NLS-1$
		String posParam = event.getParameter("com.refactorerl.ui.showFileCommand.cursorpos"); //$NON-NLS-1$
		Integer pos = null;
		try {
			pos = (posParam == null) ? null : Integer.parseInt(posParam);
		} catch (NumberFormatException e) {
			pos = null;
		}
		List<String> filepaths;
		if (pathParam == null || pathParam.isEmpty()) {

			ISelection selection = activePage.getSelection();

			if (selection == null || selection.isEmpty() || !(selection instanceof IStructuredSelection)) {
				return null;

			}

			IStructuredSelection sSelection = (IStructuredSelection) selection;

			filepaths = parseFilepaths(shell, sSelection);
			if (filepaths == null)
				return null;
		} else {
			filepaths = new ArrayList<>();
			filepaths.add(pathParam);
		}
		for (String filepath : filepaths) {
			openContentInEditor(event, activePage, filepath, pos);
		}
		return null;
	}

	/**
	 * Parses selection for file paths based on the class of the actively
	 * selected objects.
	 * 
	 * @param shell
	 * @param sSelection
	 * @return
	 */
	private List<String> parseFilepaths(Shell shell, IStructuredSelection sSelection) {
		List<String> filepaths = new LinkedList<>();

		// filepaths
		// .add(((FileListContentProvider.PathNamePair) ((TreeNode) sSelection
		// .getFirstElement()).getValue()).path);

		Iterator it = sSelection.iterator();

		// FIXME this should be handled with a command parameter and moved to
		// QueryHistoryView
		while (it.hasNext()) {

			Object nx = it.next();
			String pathString = ""; //$NON-NLS-1$
			if (nx instanceof TreeNode) {
				if (((TreeNode) nx).hasChildren())
					continue;
				pathString = ((FileListContentProvider.PathNamePair) ((TreeNode) nx).getValue()).path;
			} else if (nx instanceof QueryResultElement) {

				QueryResultElement qre = (QueryResultElement) nx;
				pathString = qre.getFileAbsolutePath();
				// System.out.println(pathString);

				if (pathString == null) {
					MessageDialog.openError(shell, Messages.ShowFileHandler_3, Messages.ShowFileHandler_4);
					return null;
				} else {
					start = qre.getStartPosition();
					end = qre.getEndPosition();
				}
			} else {
				System.out.println(nx.getClass()); // FIXME
				return null;
			}
			filepaths.add(pathString);

		}
		return filepaths;
	}

	/**
	 * This method is responsible for opening either the Erlang editor or the
	 * default Eclipse editor depending on the presence of Erlide plugin.
	 * 
	 * @param event
	 * @param activePage
	 * @param filepath
	 * @param cursorPosition
	 */
	private void openContentInEditor(final ExecutionEvent event, final IWorkbenchPage activePage, String filepath,
			final Integer cursorPosition) {
		final Shell shell = activePage.getWorkbenchWindow().getShell();

		final IEditorInput input = createEditorInput(shell, filepath);

		if (input == null)
			return;

		new UIJob(Messages.ShowFileHandler_5) {

			@Override
			public IStatus runInUIThread(IProgressMonitor monitor) {
				try {
					// open in erlide editor, if erlide is present
					boolean erlidePresent = false;
					erlidePresent = Platform.getBundle("org.erlide.ui") != null; //$NON-NLS-1$
					final ITextEditor ed;

					if (erlidePresent) {
						ed = (ITextEditor) activePage.openEditor(input,
								"org.erlide.ui.editors.scratchpad.erlangScratchPad"); //$NON-NLS-1$
						// "org.erlide.ui.editors.erl.ErlangEditor");
					} else {
						ed = (ITextEditor) activePage.openEditor(input, "org.eclipse.ui.DefaultTextEditor"); //$NON-NLS-1$
					}

					if (ed == null) {
						// // happens when some view is stacked on
						// org.eclipse.ui.editorss in
						// // org.eclipse.ui.perspectiveExtensions and the user
						// closed all editor
						// // tabs before exiting the last time (Eclipse bug).
						// // this bug has been taken care of in plugin.xml so
						// // the info dialog is not needed
						// MessageDialog.openInformation(shell,
						// "Missing editor area", "Unable to open editor, "
						// + "because editor area is missing. Please "
						// +
						// " click Window > New Window in the menu and close the old one."
						// +
						// "Try deleting the .metadata/.plugins/org.eclipse.e4.workbench/workbench.xmi "
						// +
						// "in your workspace directory if the problem persists "
						// +
						// "(this will reset perspective and possibly other settings). ");
						return Status.OK_STATUS;
					}

					if (input instanceof StringInput || input instanceof RemoteTemporaryFileEditorInput) {
						// happens when it is in the database but not in the
						// workspace

						// makes the editor read-only
						ITextViewer viewer = (ITextViewer) ((ITextEditor) ed).getAdapter(ITextOperationTarget.class);
						if (viewer != null) {
							viewer.setEditable(false);
						}
					}

					if (cursorPosition != null)
						ed.selectAndReveal(cursorPosition, 0);

					if (start >= 0 && end >= 0) {
						ed.selectAndReveal(start - 1, end - (start - 1));
						start = -1;
						end = -1;
					}

				} catch (PartInitException e) {
					Activator.showErrorDialog(e, HandlerUtil.getActiveWorkbenchWindow(event).getShell());
				}
				return Status.OK_STATUS;
			}
		}.schedule();

	}

	/**
	 * This will create an IEditorInput which can be passed to an editor as its
	 * content. The IEditorInput of the opened editor will be a FileEditorInput,
	 * if the supplied file is located in the Workspace and is added to
	 * RefactorErl. It will be a RemoteTemporaryFileEditorInput if the is added
	 * to RefactorErl, but not located in the Workspace.
	 * 
	 * @param shell
	 * @param filepath
	 * @return
	 */
	public static IEditorInput createEditorInput(Shell shell, String filepath) {

		IFile[] matches = null;
		try {
			final URI location = new URI("file:///" + filepath); //$NON-NLS-1$

			matches = ResourcesPlugin.getWorkspace().getRoot().findFilesForLocationURI(location);

		} catch (URISyntaxException | IllegalArgumentException e1) {
			Activator.showErrorDialog(new Exception(Messages.ShowFileHandler_10 + filepath), shell);
			return null;
		}

		IEditorInput input0;

		input0 = matches.length > 0 ? new FileEditorInput(matches[0]) : RemoteTemporaryFileEditorInput
				.createRemoteTemporaryFileEditorInput(filepath);
		// new StringInput(new StringStorage(filepath);

		if (input0 == null) {
			MessageDialog.openError(shell, Messages.ShowFileHandler_11, Messages.ShowFileHandler_12);
			return null;
		}

		return input0;
	}
}
