/*******************************************************************************
 * Copyright (c) 2006, 2008 Wind River Systems, IBM Corporation 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:
 * Markus Schorn - initial API and implementation
 *******************************************************************************/

package org.eclipse.search2.internal.ui.text2;

import org.eclipse.swt.custom.CCombo;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.OperationCanceledException;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.ErrorDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;

import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.widgets.FormText;
import org.eclipse.ui.model.IWorkbenchAdapter;

import org.eclipse.ui.texteditor.ITextEditor;

import org.eclipse.search.internal.ui.SearchPlugin;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.NewSearchUI;
import org.eclipse.search.ui.text.TextSearchQueryProvider;

import org.eclipse.search2.internal.ui.SearchMessages;

abstract public class RetrieverAction extends Action {
	public RetrieverAction() {
	}

	@Override
	public void run() {
		IWorkbenchPage page= getWorkbenchPage();
		if (page == null) {
			return;
		}
		TextSearchQueryProvider provider= TextSearchQueryProvider.getPreferred();
		String searchForString= getSearchForString(page);
		if (searchForString.isEmpty()) {
			MessageDialog.openInformation(getShell(), SearchMessages.RetrieverAction_dialog_title, SearchMessages.RetrieverAction_empty_selection);
			return;
		}
		try {
			ISearchQuery query= createQuery(provider, searchForString);
			if (query != null) {
				NewSearchUI.runQueryInBackground(query);
			}
		} catch (OperationCanceledException ex) {
			// action cancelled
		} catch (CoreException e) {
			ErrorDialog.openError(getShell(), SearchMessages.RetrieverAction_error_title, SearchMessages.RetrieverAction_error_message, e.getStatus());
		}
	}

	private IWorkbenchPart getActivePart() {
		IWorkbenchPage page= getWorkbenchPage();
		if (page != null) {
			return page.getActivePart();
		}
		return null;
	}

	private Shell getShell() {
		IWorkbenchPart part= getActivePart();
		if (part != null) {
			return part.getSite().getShell();
		}
		return SearchPlugin.getActiveWorkbenchShell();
	}

	abstract protected IWorkbenchPage getWorkbenchPage();
	abstract protected ISearchQuery createQuery(TextSearchQueryProvider provider, String searchForString) throws CoreException;

	final protected String extractSearchTextFromEditor(IEditorPart editor) {
		if (editor != null) {
			ITextSelection selection= null;
			ISelectionProvider provider= editor.getEditorSite().getSelectionProvider();
			if (provider != null) {
				ISelection s= provider.getSelection();
				if (s instanceof ITextSelection) {
					selection= (ITextSelection) s;
				}
			}

			if (selection != null) {
				if (selection.getLength() == 0) {
					ITextEditor txtEditor= getTextEditor(editor);
					if (txtEditor != null) {
						IDocument document= txtEditor.getDocumentProvider().getDocument(txtEditor.getEditorInput());
						selection= expandSelection(selection, document, null);
					}
				}

				if (selection.getLength() > 0 && selection.getText() != null) {
					return trimSearchString(selection.getText());
				}
			}
		}
		return null;
	}

	final protected String extractSearchTextFromSelection(ISelection sel) {
		if (sel instanceof ITextSelection) {
			String text= ((ITextSelection) sel).getText();
			if (text != null) {
				return trimSearchString(text);
			}
		} else if (sel instanceof IStructuredSelection) {
			Object firstElement= ((IStructuredSelection) sel).getFirstElement();
			if (firstElement instanceof IAdaptable) {
				IWorkbenchAdapter wbAdapter= ((IAdaptable) firstElement).getAdapter(IWorkbenchAdapter.class);
				if (wbAdapter != null) {
					return wbAdapter.getLabel(firstElement);
				}
			}
		}
		return null;
	}

	final protected String extractSearchTextFromWidget(Control control) {
		String sel= null;
		if (control instanceof Combo) {
			Combo combo= (Combo) control;
			sel= combo.getText();
			Point selection= combo.getSelection();
			sel= sel.substring(selection.x, selection.y);
		}
		if (control instanceof CCombo) {
			CCombo combo= (CCombo) control;
			sel= combo.getText();
			Point selection= combo.getSelection();
			sel= sel.substring(selection.x, selection.y);
		}
		else if (control instanceof Text) {
			Text text= (Text) control;
			sel= text.getSelectionText();
		}
		else if (control instanceof FormText) {
			FormText text= (FormText) control;
			sel= text.getSelectionText();
		}
		else if (control instanceof StyledText) {
			StyledText text= (StyledText) control;
			sel= text.getSelectionText();
		}
		else if (control instanceof Tree) {
			Tree tree= (Tree) control;
			TreeItem[] s= tree.getSelection();
			if (s.length > 0) {
				sel= s[0].getText();
			}
		}
		else if (control instanceof Table) {
			Table tree= (Table) control;
			TableItem[] s= tree.getSelection();
			if (s.length > 0) {
				sel= s[0].getText();
			}
		}
		else if (control instanceof List) {
			List list= (List) control;
			String[] s= list.getSelection();
			if (s.length > 0) {
				sel= s[0];
			}
		}

		if (sel != null) {
			sel= trimSearchString(sel);
		}
		return sel;
	}

	private String trimSearchString(String text) {
		text= text.trim();
		int idx= text.indexOf('\n');
		int idx2= text.indexOf('\r');
		if (idx2 >= 0 && idx2 < idx) {
			idx= idx2;
		}
		if (idx >= 0) {
			text= text.substring(0, idx);
		}
		return text;
	}

	private ITextEditor getTextEditor(IEditorPart editor) {
		if (editor instanceof ITextEditor) {
			return (ITextEditor) editor;
		} else
			if (editor instanceof FormEditor) {
				FormEditor me= (FormEditor) editor;
				editor= me.getActiveEditor();
				if (editor instanceof ITextEditor) {
					return (ITextEditor) editor;
				}
			}
		return null;
	}

	private ITextSelection expandSelection(ITextSelection sel, IDocument document, String stopChars) {
		int offset= sel.getOffset();
		int length= sel.getLength();

		// in case the length is zero we have to decide whether to go
		// left or right.
		if (length == 0) {
			// try right
			char chr= 0;
			char chl= 0;
			try {
				chr= document.getChar(offset);
			} catch (BadLocationException e2) {
			}
			try {
				chl= document.getChar(offset - 1);
			} catch (BadLocationException e2) {
			}

			if (isPartOfIdentifier(chr)) {
				length= 1;
			} else
				if (isPartOfIdentifier(chl)) {
					offset--;
					length= 1;
				} else
					if (stopChars != null && stopChars.indexOf(chr) == -1) {
						length= 1;
					} else
						if (stopChars != null && stopChars.indexOf(chl) == -1) {
							offset--;
							length= 1;
						} else {
							return sel;
						}
		}

		int a= offset + length - 1;
		int z= a;

		// move z one behind last character.
		try {
			char ch= document.getChar(z);
			while (isValidChar(stopChars, ch)) {
				ch= document.getChar(++z);
			}
		} catch (BadLocationException e2) {
		}
		// move a one before the first character
		try {
			char ch= document.getChar(a);
			while (isValidChar(stopChars, ch)) {
				ch= document.getChar(--a);
			}
		} catch (BadLocationException e2) {
		}

		if (a == z) {
			offset= a;
			length= 0;
		} else {
			offset= a + 1;
			length= z - a - 1;
		}
		return new TextSelection(document, offset, length);
	}

	private boolean isValidChar(String stopChars, char ch) {
		return stopChars == null ? isPartOfIdentifier(ch) : stopChars.indexOf(ch) == -1;
	}

	private boolean isPartOfIdentifier(char ch) {
		return Character.isLetterOrDigit(ch) || ch == '_';
	}

	protected String getSearchForString(IWorkbenchPage page) {
		String searchFor= extractSearchTextFromSelection(page.getSelection());
		if (searchFor == null || searchFor.isEmpty()) {
			IWorkbenchPart activePart= page.getActivePart();
			if (activePart instanceof IEditorPart) {
				searchFor= extractSearchTextFromEditor((IEditorPart) activePart);
			}
			if (searchFor == null) {
				Control focus= page.getWorkbenchWindow().getShell().getDisplay().getFocusControl();
				if (focus != null)
					searchFor= extractSearchTextFromWidget(focus);
			}
		}
		return searchFor == null ? "" : searchFor; //$NON-NLS-1$
	}

}
