/* 
-*- 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.refactoring;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;

import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IStorageEditorInput;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.CompoundContributionItem;
import org.eclipse.ui.menus.CommandContributionItem;
import org.eclipse.ui.menus.CommandContributionItemParameter;
import org.eclipse.ui.menus.IWorkbenchContribution;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.services.IServiceLocator;
import org.eclipse.ui.texteditor.ITextEditor;

import com.refactorerl.ui.logic.NodeTypeRequest;
import com.refactorerl.ui.logic.NodeTypeRequest.NodeType;
import com.refactorerl.ui.logic.queries.QueryRequest;
import com.refactorerl.ui.logic.queries.QueryResultElement;
import com.refactorerl.ui.presentation.Activator;
import com.refactorerl.ui.presentation.codebrowser.RemoteTemporaryFileEditorInput;
import com.refactorerl.ui.presentation.codebrowser.StringInput;
import com.refactorerl.ui.presentation.codebrowser.StringStorage;
import com.refactorerl.ui.presentation.refactoring.RefactoringHandler.Refactoring;


/**
 * A dynamic menu item which fills a submenu with refactoring actions depending
 * on the position of the cursor in the currently active editor.It sends
 * multiple queries to the RefactorErl server to acquire information about the
 * token under the current selection.
 * 
 * It is also responsible for creating the buttons of that submenu, and mapping
 * the {@code com.refactorerl.ui.refactor} command to them with the appropriate
 * command parameters.
 * 
 * @author Daniel Lukacs, 2014 ELTE IK
 *
 */
public class RefactoringContributionItem extends CompoundContributionItem implements IWorkbenchContribution {

	@Override
	public void initialize(IServiceLocator serviceLocator) {
		mServiceLocator = serviceLocator;

	}

	private IServiceLocator mServiceLocator;

	private class SelectionData {
		public SelectionData(NodeType type, int startPos, int endPos, String text, String filePath, boolean header) {
			super();
			this.type = type;
			this.startPos = startPos;
			this.endPos = endPos;
			this.text = text;
			this.filePath = filePath;
			this.header = header;
		}

		final public NodeType type;
		final public int startPos;
		final public int endPos;
		final public String text;
		final public String filePath;
		final public boolean header;
	}

	@Override
	protected IContributionItem[] getContributionItems() {

		List<IContributionItem> contribs = new ArrayList<>();

		SelectionData selectionData = getSelectionData();

		if (selectionData == null) {
			return new IContributionItem[0]; // error
		}

		NodeType selectionType = selectionData.type;

		switch (selectionType) {
		case FUNCTION:
			fillFunctionRefactoringItems(contribs, selectionData);
			break;
		case MACRO:
			fillMacroRefactoringItems(contribs, selectionData);
			break;
		case OVERLOADED_FUNCTION:
			fillOverloadedFunctionRefactoringItems(contribs, selectionData);
			fillFunctionRefactoringItems(contribs, selectionData);
			break;
		case RECORD:
			fillRecordRefactoringItems(contribs, selectionData);
			break;
		case RECORD_FIELD:
			fillRecordFieldRefactoringItems(contribs, selectionData);
			break;
		case VARIABLE:
			fillVariableRefactoringItems(contribs, selectionData);
			break;
		case MODULE:
			fillModuleRefactoringItems(contribs, selectionData);
			break;
		case NONE:
		default:
			break;

		}

		contribs.add(new Separator());

		fillGeneralRefactoringItems(contribs, selectionData);

		return contribs.toArray(new IContributionItem[contribs.size()]);

	}

	// NOTE this relies on the implementation of ShowFileHandler
	private SelectionData getSelectionData() {

		final IWorkbenchWindow wb = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
		IEditorPart part = wb.getActivePage().getActiveEditor();
		if (part instanceof ITextEditor) {

			final ITextEditor editor = (ITextEditor) part;

			ISelection sel = editor.getSelectionProvider().getSelection();
			if (sel instanceof TextSelection) {

				IPath path;
				IStorageEditorInput input = (IStorageEditorInput) editor.getEditorInput();

				if (input instanceof RemoteTemporaryFileEditorInput) {
					path = ((RemoteTemporaryFileEditorInput) input).getRemoteFilePath();
				} else if (input instanceof FileEditorInput) {
					path = ((FileEditorInput) input).getPath();
				} else if (input instanceof StringInput) {

					try {
						path = ((StringStorage) input.getStorage()).getFullPath();
					} catch (CoreException e1) {
						path = null; // let it crash
					}
				} else {
					path = null; // let it crash
				}
				String filePath = path.toString();

				ITextSelection textSel = (ITextSelection) sel;
				int startPos = textSel.getOffset() + 1;
				int endPos = startPos + textSel.getLength() - 1;
				NodeType nt;
				boolean header = false;

				nt = Activator.executeRequest(new NodeTypeRequest(filePath, startPos, Activator.getProxy(), null),
						wb.getShell());
				if (nt == null) {
					nt = NodeType.NONE;
				} else {
					// check if header file
					SortedMap<String, List<QueryResultElement>> res = Activator.executeRequest(new QueryRequest(
							filePath, "@file.header", false, Activator.getProxy(), null), wb.getShell()); //$NON-NLS-1$

					header = res.get(res.firstKey()).get(0).getValue().equals("true"); //$NON-NLS-1$
				}

				return new SelectionData(nt, startPos, endPos, textSel.getText(), filePath, header);
			}
		}

		return null;
	}

	private CommandContributionItem createCommandContributionItem(RefactoringHandler.Refactoring refactorNameParameter,
			String label, SelectionData selectionData) {

		Map<String, String> parameters = new HashMap<>();
		parameters.put("com.refactorerl.ui.refactorNameParameter", refactorNameParameter.toString()); //$NON-NLS-1$
		parameters.put("com.refactorerl.ui.refactorStartPosParameter", new Integer(selectionData.startPos).toString()); //$NON-NLS-1$
		parameters.put("com.refactorerl.ui.refactorEndPosParameter", new Integer(selectionData.endPos).toString()); //$NON-NLS-1$
		parameters.put("com.refactorerl.ui.refactorFilePathParameter", selectionData.filePath); //$NON-NLS-1$

		final CommandContributionItemParameter contributionParameter = new CommandContributionItemParameter(
				mServiceLocator, null, "com.refactorerl.ui.refactor", CommandContributionItem.STYLE_PUSH); //$NON-NLS-1$
		contributionParameter.parameters = parameters;
		contributionParameter.label = label;
		contributionParameter.visibleEnabled = true;

		return new CommandContributionItem(contributionParameter);
	}

	private void fillGeneralRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		if (selectionData.header)
			contribs.add(createCommandContributionItem(Refactoring.RENAME_HEADER,
					Messages.RefactoringContributionItem_7, selectionData));
		else
			contribs.add(createCommandContributionItem(Refactoring.RENAME_MODULE,
					Messages.RefactoringContributionItem_8, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.RENAME_UNUSED_VARIABLES,
				Messages.RefactoringContributionItem_9, selectionData));

		// NOTE the following are not general, but there is no type for them

		contribs.add(createCommandContributionItem(Refactoring.EXPAND_FUNCTION_EXPRESSION,
				Messages.RefactoringContributionItem_10, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.EXTRACT_FUNCTION,
				Messages.RefactoringContributionItem_11, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.TUPLE_FUNCTION_PARAMETERS,
				Messages.RefactoringContributionItem_12, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.MERGE_EXPRESSION,
				Messages.RefactoringContributionItem_13, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.TOGGLE_LIST_COMPREHENSION,
				Messages.RefactoringContributionItem_14, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.INTRODUCE_RECORD,
				Messages.RefactoringContributionItem_15, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.ELIMINATE_IMPORT,
				Messages.RefactoringContributionItem_16, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.GENERALIZE_FUNCTION,
				Messages.RefactoringContributionItem_17, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.UPGRADE_REGEXP, Messages.RefactoringContributionItem_18,
				selectionData));

	}

	private void fillFunctionRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {

		contribs.add(createCommandContributionItem(Refactoring.RENAME_FUNCTION,
				Messages.RefactoringContributionItem_19, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.INLINE_FUNCTION,
				Messages.RefactoringContributionItem_20, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.REORDER_FUNCTION_PARAMETERS,
				Messages.RefactoringContributionItem_21, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.MOVE_FUNCTION, Messages.RefactoringContributionItem_22,
				selectionData));

		contribs.add(createCommandContributionItem(Refactoring.FUNAPP_TO_PROC, Messages.RefactoringContributionItem_23,
				selectionData));
	
		contribs.add(createCommandContributionItem(Refactoring.INTRODUCE_IMPORT,
				Messages.RefactoringContributionItem_24, selectionData));

	}

	private void fillMacroRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		contribs.add(createCommandContributionItem(Refactoring.RENAME_MACRO, Messages.RefactoringContributionItem_25,
				selectionData));

		contribs.add(createCommandContributionItem(Refactoring.INLINE_MACRO, Messages.RefactoringContributionItem_26,
				selectionData));

		contribs.add(createCommandContributionItem(Refactoring.MOVE_MACRO, Messages.RefactoringContributionItem_27,
				selectionData));

	}

	private void fillOverloadedFunctionRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		contribs.add(createCommandContributionItem(Refactoring.RENAME_OVERLOADED_FUNCTIONS,
				Messages.RefactoringContributionItem_28, selectionData));

	}

	private void fillRecordRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		contribs.add(createCommandContributionItem(Refactoring.RENAME_RECORD, Messages.RefactoringContributionItem_29,
				selectionData));

		contribs.add(createCommandContributionItem(Refactoring.MOVE_RECORD, Messages.RefactoringContributionItem_30,
				selectionData));

	}

	private void fillRecordFieldRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		contribs.add(createCommandContributionItem(Refactoring.RENAME_RECORD_FIELD,
				Messages.RefactoringContributionItem_31, selectionData));
	}

	private void fillVariableRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {
		contribs.add(createCommandContributionItem(Refactoring.RENAME_VARIABLE,
				Messages.RefactoringContributionItem_32, selectionData));

		contribs.add(createCommandContributionItem(Refactoring.ELIMINATE_VARIABLE,
				Messages.RefactoringContributionItem_33, selectionData));

	}

	private void fillModuleRefactoringItems(List<IContributionItem> contribs, SelectionData selectionData) {

		contribs.add(createCommandContributionItem(Refactoring.INTRODUCE_IMPORT,
				Messages.RefactoringContributionItem_34, selectionData));
	}

}
