/*******************************************************************************
 * Copyright (c) 2013 Ericsson AB 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:
 *     Alvaro Sanchez-Leon (Ericsson AB) - Support for Step into selection (bug 244865)
 *******************************************************************************/
package org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup;

import org.eclipse.cdt.core.model.IFunctionDeclaration;
import org.eclipse.cdt.debug.internal.ui.CDebugUIUtils;
import org.eclipse.cdt.dsf.internal.ui.DsfUIPlugin;
import org.eclipse.cdt.internal.ui.editor.CEditor;
import org.eclipse.cdt.internal.ui.editor.SelectionToDeclarationJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbench;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.texteditor.ITextEditor;

public class DsfSourceSelectionResolver implements Runnable  {
	private ITextEditor fEditorPage = null;
	private ITextSelection fSelection = null;
	private LineLocation fLineLocation = new LineLocation();
	private IFunctionDeclaration fFunction = null;
	private boolean fSuccessful = false;

	public class LineLocation {
		private String fileName = null;
		private int lineNumber = 0;
	
		public String getFileName() {
			return fileName;
		}
	
		public void setFileName(String fileName) {
			this.fileName = fileName;
		}
	
		public int getLineNumber() {
			return lineNumber;
		}
	
		public void setLineNumber(int lineNumber) {
			this.lineNumber = lineNumber;
		}
	}

	private interface ResolveEditorRunnable extends Runnable {
		TextEditor getEditor();
	}

	public DsfSourceSelectionResolver() {
		
	}

	public DsfSourceSelectionResolver(ITextEditor editor, ITextSelection selection) {
		fEditorPage = editor;
		fSelection = selection;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.cdt.dsf.debug.internal.ui.sourcelookup.IDsfSourceSelectionResolver#run()
	 */
	@Override
	public void run() {
		fEditorPage = resolveEditor();
		if (fEditorPage != null) {
			ITextSelection selection = resolveSelection();
			if (selection != null) {
				IFunctionDeclaration[] selectedFunctions = resolveSelectedFunction(selection);

				IFunctionDeclaration selFunction = null;
				if (selectedFunctions == null || selectedFunctions.length != 1 || selectedFunctions[0] == null) {
					//Unable to resolve selection to a function
					return;
				} else {
					// Continue as expected
					selFunction = selectedFunctions[0];
				}

				LineLocation selectedLine = resolveSelectedLine();
				if (selectedLine == null) {
					// Unable to resolve the selected line
					return;
				}

				fLineLocation = selectedLine;
				fFunction = selFunction;
				fSuccessful = true;
			}
		}
	}

	public ITextEditor resolveEditor() {
		if (fEditorPage != null) {
			return fEditorPage;
		}
		
		final IWorkbench wb = DsfUIPlugin.getDefault().getWorkbench();
		// Run in UI thread to access UI resources
		ResolveEditorRunnable reditorRunnable = new ResolveEditorRunnable() {
			TextEditor result = null;

			@Override
			public void run() {
				IWorkbenchWindow win = wb.getActiveWorkbenchWindow();
				if (win == null || win.getActivePage() == null || win.getActivePage().getActiveEditor() == null) {
					result = null;
				} else {
					IEditorPart editorPart = win.getActivePage().getActiveEditor();
					if (editorPart instanceof CEditor) {
						result = (TextEditor) win.getActivePage().getActiveEditor();
					}
				}
			}

			@Override
			public TextEditor getEditor() {
				return result;
			}
		};

		Display.getDefault().syncExec(reditorRunnable);
		return reditorRunnable.getEditor();
	}		
	
	private LineLocation resolveSelectedLine() {
		String errorMessage = ""; //$NON-NLS-1$
		IEditorInput input = fEditorPage.getEditorInput();
		if (input == null) {
			errorMessage = "Invalid Editor input on selection"; //$NON-NLS-1$				
		} else {
			IDocument document = fEditorPage.getDocumentProvider().getDocument(input);
			if (document == null) {
				errorMessage = "Invalid Editor Document input on selection"; //$NON-NLS-1$
			} else {
				ITextSelection selection = resolveSelection();
				if (selection == null) {
					errorMessage = "Invalid selection. Only textual selections are supported"; //$NON-NLS-1$
				} else {
					String fileName = null;
					try {
						fileName = CDebugUIUtils.getEditorFilePath(input);
					} catch (CoreException e) {
						// unable to resolve the path
						DsfUIPlugin.log(e);
						return null;
					}

					if (fileName == null) {
						errorMessage = "Unable to resolve fileName from selection"; //$NON-NLS-1$
						DsfUIPlugin.logErrorMessage(errorMessage);
					} else {
						// Resolve the values
						LineLocation lineLocation = new LineLocation();

						lineLocation.setFileName(fileName);
						lineLocation.setLineNumber(selection.getStartLine() + 1);
						return lineLocation;
					}
				}
			}
		}

		DsfUIPlugin.logErrorMessage(errorMessage);
		return null;
	}
	
	public ITextSelection resolveSelection() {
		if (fSelection != null) {
			//Value received at construction time
			return fSelection;
		}
		
		ISelection selection = fEditorPage.getEditorSite().getSelectionProvider().getSelection();
		if (selection instanceof ITextSelection) {
			return (ITextSelection) selection;
		}
		
		return null;
	}
	
	private IFunctionDeclaration[] resolveSelectedFunction(ITextSelection textSelection) {
		if (textSelection != null) {
			SelectionToDeclarationJob job;
			try {
				job = new SelectionToDeclarationJob(fEditorPage, textSelection);
				job.schedule();
				job.join();
			} catch (CoreException e1) {
				DsfUIPlugin.log(e1);
				return null;
			} catch (InterruptedException e) {
				DsfUIPlugin.log(e);
				return null;
			}
			
			//fetch the result 
			return job.getSelectedFunctions();
		}
		
		return null;
	}

	public LineLocation getLineLocation() {
		return fLineLocation;
	}

	public IFunctionDeclaration getFunction() {
		return fFunction;
	}

	public boolean isSuccessful() {
		return fSuccessful;
	}
}
