/*******************************************************************************
 * Copyright (c) 2000, 2018 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:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.ui.refactoring;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

import org.eclipse.core.runtime.Assert;

import org.eclipse.jface.contentassist.SubjectControlContentAssistant;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITableFontProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;

import org.eclipse.ui.contentassist.ContentAssistHandler;

import org.eclipse.jdt.core.IJavaProject;

import org.eclipse.jdt.internal.core.manipulation.StubUtility;
import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
import org.eclipse.jdt.internal.corext.refactoring.StubTypeContext;

import org.eclipse.jdt.internal.ui.dialogs.TableTextCellEditor;
import org.eclipse.jdt.internal.ui.dialogs.TextFieldNavigationHandler;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.ControlContentAssistHelper;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.JavaTypeCompletionProcessor;
import org.eclipse.jdt.internal.ui.refactoring.contentassist.VariableNamesProcessor;
import org.eclipse.jdt.internal.ui.util.SWTUtil;
import org.eclipse.jdt.internal.ui.util.TableLayoutComposite;

/**
 * A special control to edit and reorder method parameters.
 */
public class ChangeParametersControl extends Composite {

	public static class Mode {
		private final String fName;
		private Mode(String name) {
			fName= name;
		}
		public static final Mode EXTRACT_METHOD= new Mode("EXTRACT_METHOD"); //$NON-NLS-1$
		public static final Mode CHANGE_METHOD_SIGNATURE= new Mode("CHANGE_METHOD_SIGNATURE"); //$NON-NLS-1$
		public static final Mode INTRODUCE_PARAMETER= new Mode("INTRODUCE_PARAMETER"); //$NON-NLS-1$
		@Override
		public String toString() {
			return fName;
		}
		public boolean canChangeTypes() {
			return this == CHANGE_METHOD_SIGNATURE;
		}
		public boolean canAddParameters() {
			return this == Mode.CHANGE_METHOD_SIGNATURE;
		}
		public boolean canChangeDefault() {
			return this == Mode.CHANGE_METHOD_SIGNATURE;
		}
	}

	private static class ParameterInfoContentProvider implements IStructuredContentProvider {
		@Override
		@SuppressWarnings("unchecked")
		public Object[] getElements(Object inputElement) {
			return removeMarkedAsDeleted((List<ParameterInfo>) inputElement);
		}
		private ParameterInfo[] removeMarkedAsDeleted(List<ParameterInfo> paramInfos){
			List<ParameterInfo> result= new ArrayList<>(paramInfos.size());
			for (ParameterInfo info : paramInfos) {
				if (! info.isDeleted())
					result.add(info);
			}
			return result.toArray(new ParameterInfo[result.size()]);
		}
		@Override
		public void dispose() {
			// do nothing
		}
		@Override
		public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
			// do nothing
		}
	}

	private static class ParameterInfoLabelProvider extends LabelProvider implements ITableLabelProvider, ITableFontProvider {
		@Override
		public Image getColumnImage(Object element, int columnIndex) {
			return null;
		}
		@Override
		public String getColumnText(Object element, int columnIndex) {
			ParameterInfo info= (ParameterInfo) element;
			switch (columnIndex) {
				case TYPE_PROP:
					return info.getNewTypeName();
				case NEWNAME_PROP:
					return info.getNewName();
				case DEFAULT_PROP:
				    if (info.isAdded())
				        return info.getDefaultValue();
				    else
				        return "-"; //$NON-NLS-1$
				default:
					throw new IllegalArgumentException(columnIndex + ": " + element); //$NON-NLS-1$
			}
		}
		@Override
		public Font getFont(Object element, int columnIndex) {
			ParameterInfo info= (ParameterInfo) element;
			if (info.isAdded())
				return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT);
			else
				return null;
		}
	}

	private class ParametersCellModifier implements ICellModifier {
		@Override
		public boolean canModify(Object element, String property) {
			Assert.isTrue(element instanceof ParameterInfo);
			if (property.equals(PROPERTIES[TYPE_PROP]))
				return fMode.canChangeTypes();
			else if (property.equals(PROPERTIES[NEWNAME_PROP]))
				return true;
			else if (property.equals(PROPERTIES[DEFAULT_PROP]))
				return (((ParameterInfo)element).isAdded());
			Assert.isTrue(false);
			return false;
		}
		@Override
		public Object getValue(Object element, String property) {
			Assert.isTrue(element instanceof ParameterInfo);
			if (property.equals(PROPERTIES[TYPE_PROP]))
				return ((ParameterInfo) element).getNewTypeName();
			else if (property.equals(PROPERTIES[NEWNAME_PROP]))
				return ((ParameterInfo) element).getNewName();
			else if (property.equals(PROPERTIES[DEFAULT_PROP]))
				return ((ParameterInfo) element).getDefaultValue();
			Assert.isTrue(false);
			return null;
		}
		@Override
		public void modify(Object element, String property, Object value) {
			if (element instanceof TableItem)
				element= ((TableItem) element).getData();
			if (!(element instanceof ParameterInfo))
				return;
			boolean unchanged;
			ParameterInfo parameterInfo= (ParameterInfo) element;
			if (property.equals(PROPERTIES[NEWNAME_PROP])) {
				unchanged= parameterInfo.getNewName().equals(value);
				parameterInfo.setNewName((String) value);
			} else if (property.equals(PROPERTIES[DEFAULT_PROP])) {
				unchanged= parameterInfo.getDefaultValue().equals(value);
				parameterInfo.setDefaultValue((String) value);
			} else if (property.equals(PROPERTIES[TYPE_PROP])) {
				unchanged= parameterInfo.getNewTypeName().equals(value);
				parameterInfo.setNewTypeName((String) value);
			} else {
				throw new IllegalStateException();
			}
			if (! unchanged) {
				ChangeParametersControl.this.fListener.parameterChanged(parameterInfo);
				ChangeParametersControl.this.fTableViewer.update(parameterInfo, new String[] { property });
			}
		}
	}

	private static final String[] PROPERTIES= { "type", "new", "default" }; //$NON-NLS-2$ //$NON-NLS-1$ //$NON-NLS-3$
	private static final int TYPE_PROP= 0;
	private static final int NEWNAME_PROP= 1;
	private static final int DEFAULT_PROP= 2;

	private static final int ROW_COUNT= 7;

	private final Mode fMode;
	private final IParameterListChangeListener fListener;
	private List<ParameterInfo> fParameterInfos;
	private final StubTypeContext fTypeContext;
	private final String[] fParamNameProposals;
	private ContentAssistHandler fNameContentAssistHandler;

	private TableViewer fTableViewer;
	private Button fUpButton;
	private Button fDownButton;
	private Button fEditButton;
	private Button fAddButton;
	private Button fRemoveButton;

	public ChangeParametersControl(Composite parent, int style, String label, IParameterListChangeListener listener, Mode mode, StubTypeContext typeContext) {
		this(parent, style, label, listener, mode, typeContext, new String[0]);
	}

	public ChangeParametersControl(Composite parent, int style, String label, IParameterListChangeListener listener, Mode mode) {
		this(parent, style, label, listener, mode, null, new String[0]);
	}

	public ChangeParametersControl(Composite parent, int style, String label, IParameterListChangeListener listener, Mode mode, String[] paramNameProposals) {
		this(parent, style, label, listener, mode, null, paramNameProposals);
	}

	/**
	 * @param label the label before the table or <code>null</code>
	 * @param typeContext the package in which to complete types
	 */
	private ChangeParametersControl(Composite parent, int style, String label, IParameterListChangeListener listener, Mode mode, StubTypeContext typeContext, String[] paramNameProposals) {
		super(parent, style);
		Assert.isNotNull(listener);
		fListener= listener;
		fMode= mode;
		fTypeContext= typeContext;
		fParamNameProposals= paramNameProposals;

		GridLayout layout= new GridLayout();
		layout.numColumns= 2;
		layout.marginWidth= 0;
		layout.marginHeight= 0;
		setLayout(layout);

		if (label != null) {
			Label tableLabel= new Label(this, SWT.NONE);
			GridData labelGd= new GridData();
			labelGd.horizontalSpan= 2;
			tableLabel.setLayoutData(labelGd);
			tableLabel.setText(label);
		}

		createParameterList(this);
		createButtonComposite(this);
	}


	public void setInput(List<ParameterInfo> parameterInfos) {
		Assert.isNotNull(parameterInfos);
		fParameterInfos= parameterInfos;
		fTableViewer.setInput(fParameterInfos);
		if (fParameterInfos.size() > 0)
			fTableViewer.setSelection(new StructuredSelection(fParameterInfos.get(0)));
	}

	public void editParameter(ParameterInfo info) {
		fTableViewer.getControl().setFocus();
		if (! info.isDeleted()) {
			fTableViewer.setSelection(new StructuredSelection(info), true);
			updateButtonsEnabledState();
			editColumnOrNextPossible(NEWNAME_PROP);
			return;
		}
	}

	// ---- Parameter table -----------------------------------------------------------------------------------

	private void createParameterList(Composite parent) {
		TableLayoutComposite layouter= new TableLayoutComposite(parent, SWT.NONE);
		addColumnLayoutData(layouter);

		final Table table= new Table(layouter, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
		table.setHeaderVisible(true);
		table.setLinesVisible(true);
		TableColumn tc;
		tc= new TableColumn(table, SWT.NONE, TYPE_PROP);
		tc.setResizable(true);
		tc.setText(RefactoringMessages.ChangeParametersControl_table_type);

		tc= new TableColumn(table, SWT.NONE, NEWNAME_PROP);
		tc.setResizable(true);
		tc.setText(RefactoringMessages.ChangeParametersControl_table_name);

		if (fMode.canChangeDefault()){
			tc= new TableColumn(table, SWT.NONE, DEFAULT_PROP);
			tc.setResizable(true);
			tc.setText(RefactoringMessages.ChangeParametersControl_table_defaultValue);
		}

		GridData gd= new GridData(GridData.FILL_BOTH);
		gd.heightHint= SWTUtil.getTableHeightHint(table, ROW_COUNT);
		gd.widthHint= 40;
		layouter.setLayoutData(gd);

		fTableViewer= new TableViewer(table);
		fTableViewer.setUseHashlookup(true);
		fTableViewer.setContentProvider(new ParameterInfoContentProvider());
		fTableViewer.setLabelProvider(new ParameterInfoLabelProvider());
		fTableViewer.addSelectionChangedListener(event -> updateButtonsEnabledState());

		table.addTraverseListener(e -> {
			if (e.detail == SWT.TRAVERSE_RETURN && e.stateMask == SWT.NONE) {
				editColumnOrNextPossible(0);
				e.detail= SWT.TRAVERSE_NONE;
			}
		});
		table.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent e) {
				if (e.keyCode == SWT.F2 && e.stateMask == SWT.NONE) {
					editColumnOrNextPossible(0);
					e.doit= false;
				}
			}
		});

		addCellEditors();
	}

	private void editColumnOrNextPossible(int column){
		ParameterInfo[]	selected= getSelectedElements();
		if (selected.length != 1)
			return;
		int nextColumn= column;
		do {
			fTableViewer.editElement(selected[0], nextColumn);
			if (fTableViewer.isCellEditorActive())
				return;
			nextColumn= nextColumn(nextColumn);
		} while (nextColumn != column);
	}

	private void editColumnOrPrevPossible(int column){
		ParameterInfo[]	selected= getSelectedElements();
		if (selected.length != 1)
			return;
		int prevColumn= column;
		do {
			fTableViewer.editElement(selected[0], prevColumn);
			if (fTableViewer.isCellEditorActive())
			    return;
			prevColumn= prevColumn(prevColumn);
		} while (prevColumn != column);
	}

	private int nextColumn(int column) {
		return (column >= getTable().getColumnCount() - 1) ? 0 : column + 1;
	}

	private int prevColumn(int column) {
		return (column <= 0) ? getTable().getColumnCount() - 1 : column - 1;
	}

	private void addColumnLayoutData(TableLayoutComposite layouter) {
		if (fMode.canChangeDefault()){
			layouter.addColumnData(new ColumnWeightData(33, true));
			layouter.addColumnData(new ColumnWeightData(33, true));
			layouter.addColumnData(new ColumnWeightData(34, true));
		} else {
			layouter.addColumnData(new ColumnWeightData(50, true));
			layouter.addColumnData(new ColumnWeightData(50, true));
		}
	}

	private ParameterInfo[] getSelectedElements() {
		ISelection selection= fTableViewer.getSelection();
		if (selection == null)
			return new ParameterInfo[0];

		if (!(selection instanceof IStructuredSelection))
			return new ParameterInfo[0];

		List<?> selected= ((IStructuredSelection) selection).toList();
		return selected.toArray(new ParameterInfo[selected.size()]);
	}

	// ---- Button bar --------------------------------------------------------------------------------------

	private void createButtonComposite(Composite parent) {
		Composite buttonComposite= new Composite(parent, SWT.NONE);
		buttonComposite.setLayoutData(new GridData(GridData.FILL_VERTICAL));
		GridLayout gl= new GridLayout();
		gl.marginHeight= 0;
		gl.marginWidth= 0;
		buttonComposite.setLayout(gl);

		if (fMode.canAddParameters())
			fAddButton= createAddButton(buttonComposite);

		fEditButton= createEditButton(buttonComposite);

		if (fMode.canAddParameters())
			fRemoveButton= createRemoveButton(buttonComposite);

		if (buttonComposite.getChildren().length != 0)
			addSpacer(buttonComposite);

		fUpButton= createButton(buttonComposite, RefactoringMessages.ChangeParametersControl_buttons_move_up, true);
		fDownButton= createButton(buttonComposite, RefactoringMessages.ChangeParametersControl_buttons_move_down, false);

		updateButtonsEnabledState();
	}

	private void addSpacer(Composite parent) {
		Label label= new Label(parent, SWT.NONE);
		GridData gd= new GridData(GridData.FILL_HORIZONTAL);
		gd.heightHint= 5;
		label.setLayoutData(gd);
	}

	private void updateButtonsEnabledState() {
		fUpButton.setEnabled(canMove(true));
		fDownButton.setEnabled(canMove(false));
		if (fEditButton != null)
			fEditButton.setEnabled(getTableSelectionCount() == 1);
		if (fAddButton != null)
			fAddButton.setEnabled(true);
		if (fRemoveButton != null)
			fRemoveButton.setEnabled(getTableSelectionCount() != 0);
	}

	private int getTableSelectionCount() {
		return getTable().getSelectionCount();
	}

	private int getTableItemCount() {
		return getTable().getItemCount();
	}

	private Table getTable() {
		return fTableViewer.getTable();
	}

	private Button createEditButton(Composite buttonComposite) {
		Button button= new Button(buttonComposite, SWT.PUSH);
		button.setText(RefactoringMessages.ChangeParametersControl_buttons_edit);
		button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		SWTUtil.setButtonDimensionHint(button);
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				try {
					ParameterInfo[] selected= getSelectedElements();
					Assert.isTrue(selected.length == 1);
					ParameterInfo parameterInfo= selected[0];
					ParameterEditDialog dialog= new ParameterEditDialog(getShell(), parameterInfo, fMode.canChangeTypes(), fMode.canChangeDefault(), fTypeContext);
					dialog.open();
					fListener.parameterChanged(parameterInfo);
					fTableViewer.update(parameterInfo, PROPERTIES);
				} finally {
					fTableViewer.getControl().setFocus();
				}
			}
		});
		return button;
	}

	private Button createAddButton(Composite buttonComposite) {
		Button button= new Button(buttonComposite, SWT.PUSH);
		button.setText(RefactoringMessages.ChangeParametersControl_buttons_add);
		button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		SWTUtil.setButtonDimensionHint(button);
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				String[] excludedParamNames= new String[fParameterInfos.size()];
				for (int i= 0; i < fParameterInfos.size(); i++) {
					ParameterInfo info= fParameterInfos.get(i);
					excludedParamNames[i]= info.getNewName();
				}
				IJavaProject javaProject= fTypeContext.getCuHandle().getJavaProject();
				String newParamName= StubUtility.suggestArgumentName(javaProject, RefactoringMessages.ChangeParametersControl_new_parameter_default_name, excludedParamNames);
				ParameterInfo newInfo= ParameterInfo.createInfoForAddedParameter("Object", newParamName, "null"); //$NON-NLS-1$ //$NON-NLS-2$
				int insertIndex= fParameterInfos.size();
				for (int i= fParameterInfos.size() - 1;  i >= 0; i--) {
					ParameterInfo info= fParameterInfos.get(i);
					if (info.isNewVarargs()) {
						insertIndex= i;
						break;
					}
				}
				fParameterInfos.add(insertIndex, newInfo);
				fListener.parameterAdded(newInfo);
				fTableViewer.refresh();
				fTableViewer.getControl().setFocus();
				fTableViewer.setSelection(new StructuredSelection(newInfo), true);
				updateButtonsEnabledState();
				editColumnOrNextPossible(0);
			}
		});
		return button;
	}

	private Button createRemoveButton(Composite buttonComposite) {
		final Button button= new Button(buttonComposite, SWT.PUSH);
		button.setText(RefactoringMessages.ChangeParametersControl_buttons_remove);
		button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		SWTUtil.setButtonDimensionHint(button);
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				int index= getTable().getSelectionIndices()[0];
				for (ParameterInfo s : getSelectedElements()) {
					if (s.isAdded()) {
						fParameterInfos.remove(s);
					} else {
						s.markAsDeleted();
					}
				}
				restoreSelection(index);
			}
			private void restoreSelection(int index) {
				fTableViewer.refresh();
				fTableViewer.getControl().setFocus();
				int itemCount= getTableItemCount();
				if (itemCount != 0) {
				    if (index >= itemCount)
				        index= itemCount - 1;
				    getTable().setSelection(index);
				}
				fListener.parameterListChanged();
				updateButtonsEnabledState();
			}
		});
		return button;
	}

	private Button createButton(Composite buttonComposite, String text, final boolean up) {
		Button button= new Button(buttonComposite, SWT.PUSH);
		button.setText(text);
		button.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		SWTUtil.setButtonDimensionHint(button);
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				ISelection savedSelection= fTableViewer.getSelection();
				if (savedSelection == null)
					return;
				ParameterInfo[] selection= getSelectedElements();
				if (selection.length == 0)
					return;

				if (up) {
					moveUp(selection);
				} else {
					moveDown(selection);
				}
				fTableViewer.refresh();
				fTableViewer.setSelection(savedSelection);
				fListener.parameterListChanged();
				fTableViewer.getControl().setFocus();
			}
		});
		return button;
	}

	//---- editing -----------------------------------------------------------------------------------------------

	private void addCellEditors() {
		fTableViewer.setColumnProperties(PROPERTIES);

		final TableTextCellEditor editors[]= new TableTextCellEditor[PROPERTIES.length];

		editors[TYPE_PROP]= new TableTextCellEditor(fTableViewer, TYPE_PROP);
		editors[NEWNAME_PROP]= new TableTextCellEditor(fTableViewer, NEWNAME_PROP);
		editors[DEFAULT_PROP]= new TableTextCellEditor(fTableViewer, DEFAULT_PROP);

		if (fMode.canChangeTypes()) {
			SubjectControlContentAssistant assistant= installParameterTypeContentAssist(editors[TYPE_PROP].getText());
			editors[TYPE_PROP].setContentAssistant(assistant);
		}
		if (fParamNameProposals.length > 0) {
			SubjectControlContentAssistant assistant= installParameterNameContentAssist(editors[NEWNAME_PROP].getText());
			editors[NEWNAME_PROP].setContentAssistant(assistant);
		}

		for (int i = 0; i < editors.length; i++) {
			final int editorColumn= i;
			final TableTextCellEditor editor = editors[i];
			// support tabbing between columns while editing:
			editor.getText().addTraverseListener(e -> {
				switch (e.detail) {
					case SWT.TRAVERSE_TAB_NEXT :
						editColumnOrNextPossible(nextColumn(editorColumn));
						e.detail= SWT.TRAVERSE_NONE;
						break;

					case SWT.TRAVERSE_TAB_PREVIOUS :
						editColumnOrPrevPossible(prevColumn(editorColumn));
						e.detail= SWT.TRAVERSE_NONE;
						break;

					default :
						break;
				}
			});
			TextFieldNavigationHandler.install(editor.getText());
		}

		editors[NEWNAME_PROP].setActivationListener(() -> {
			ParameterInfo[] selected= getSelectedElements();
			if (selected.length == 1 && fNameContentAssistHandler != null) {
				fNameContentAssistHandler.setEnabled(selected[0].isAdded());
			}
		});

		fTableViewer.setCellEditors(editors);
		fTableViewer.setCellModifier(new ParametersCellModifier());
	}

	private SubjectControlContentAssistant installParameterTypeContentAssist(Text text) {
		JavaTypeCompletionProcessor processor= new JavaTypeCompletionProcessor(true, false);
		if (fTypeContext == null)
			processor.setCompletionContext(null, null, null);
		else
			processor.setCompletionContext(fTypeContext.getCuHandle(), fTypeContext.getBeforeString(), fTypeContext.getAfterString());
		SubjectControlContentAssistant contentAssistant= ControlContentAssistHelper.createJavaContentAssistant(processor);
		ContentAssistHandler.createHandlerForText(text, contentAssistant);
		return contentAssistant;
	}

	private SubjectControlContentAssistant installParameterNameContentAssist(Text text) {
		VariableNamesProcessor processor= new VariableNamesProcessor(fParamNameProposals);
		SubjectControlContentAssistant contentAssistant= ControlContentAssistHelper.createJavaContentAssistant(processor);
		fNameContentAssistHandler= ContentAssistHandler.createHandlerForText(text, contentAssistant);
		return contentAssistant;
	}

	//---- change order ----------------------------------------------------------------------------------------

	private void moveUp(ParameterInfo[] selection) {
		moveUp(fParameterInfos, Arrays.asList(selection));
	}

	private void moveDown(ParameterInfo[] selection) {
		Collections.reverse(fParameterInfos);
		moveUp(fParameterInfos, Arrays.asList(selection));
		Collections.reverse(fParameterInfos);
	}

	private static void moveUp(List<ParameterInfo> elements, List<ParameterInfo> move) {
		List<ParameterInfo> res= new ArrayList<>(elements.size());
		List<ParameterInfo> deleted= new ArrayList<>();
		ParameterInfo floating= null;
		for (ParameterInfo curr : elements) {
			if (move.contains(curr)) {
				res.add(curr);
			} else if (curr.isDeleted()) {
				deleted.add(curr);
			} else {
				if (floating != null)
					res.add(floating);
				floating= curr;
			}
		}
		if (floating != null) {
			res.add(floating);
		}
		res.addAll(deleted);
		elements.clear();
		elements.addAll(res);
	}

	private boolean canMove(boolean up) {
		int notDeletedInfosCount= getNotDeletedInfosCount();
		if (notDeletedInfosCount == 0)
			return false;
		int[] indc= getTable().getSelectionIndices();
		if (indc.length == 0)
			return false;
		int invalid= up ? 0 : notDeletedInfosCount - 1;
		for (int element : indc) {
			if (element == invalid)
				return false;
		}
		return true;
	}

	private int getNotDeletedInfosCount(){
		if (fParameterInfos == null) // during initialization
			return 0;
		int result= 0;
		for (ParameterInfo info : fParameterInfos) {
			if (! info.isDeleted())
				result++;
		}
		return result;
	}
}
