/*******************************************************************************
 * Copyright (c) 2005, 2015 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
 *     QNX Software System
 *******************************************************************************/
package org.eclipse.cdt.internal.autotools.ui.editors.automake;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

/**
 * A class to select one or more elements out of an indexed property
 */
public abstract class AbstractElementListSelectionDialog extends SelectionStatusDialog {

	private ILabelProvider fRenderer;
	private boolean fIgnoreCase;
	private boolean fIsMultipleSelection;

	private SelectionList fSelectionList;
	private Label fMessage;
	private ISelectionValidator fValidator;

	private String fMessageText;
	private String fEmptyListMessage;
	private String fNothingSelectedMessage;

	private StatusInfo fCurrStatus;

	protected void access$superOpen() {
		super.open();
	}

	@Override
	protected void cancelPressed() {
		setResult(null);
		super.cancelPressed();
	}

	protected Point computeInitialSize() {
		return new Point(convertWidthInCharsToPixels(60), convertHeightInCharsToPixels(18));
	}

	@Override
	protected Control createDialogArea(Composite parent) {
		Composite contents = (Composite) super.createDialogArea(parent);

		fMessage = createMessage(contents);

		int flags = fIsMultipleSelection ? SWT.MULTI : SWT.SINGLE;
		fSelectionList = new SelectionList(contents, flags | SWT.BORDER | SWT.V_SCROLL | SWT.H_SCROLL, fRenderer,
				fIgnoreCase);

		fSelectionList.addSelectionListener(new SelectionListener() {
			@Override
			public void widgetDefaultSelected(SelectionEvent e) {
				handleDoubleClick();
			}

			@Override
			public void widgetSelected(SelectionEvent e) {
				verifyCurrentSelection();
			}
		});

		GridData spec = new GridData();
		Point initialSize = computeInitialSize();
		spec.widthHint = initialSize.x;
		spec.heightHint = initialSize.y;
		spec.grabExcessVerticalSpace = true;
		spec.grabExcessHorizontalSpace = true;
		spec.horizontalAlignment = GridData.FILL;
		spec.verticalAlignment = GridData.FILL;
		fSelectionList.setLayoutData(spec);

		return contents;
	}

	/**
	 * Creates the message text widget and sets layout data.
	 */
	protected Label createMessage(Composite parent) {
		Label text = new Label(parent, SWT.NULL);
		text.setText(fMessageText);
		GridData spec = new GridData();
		spec.grabExcessVerticalSpace = false;
		spec.grabExcessHorizontalSpace = true;
		spec.horizontalAlignment = GridData.FILL;
		spec.verticalAlignment = GridData.BEGINNING;
		text.setLayoutData(spec);
		return text;
	}

	@Override
	public void create() {
		super.create();
		if (isEmptyList()) {
			fMessage.setEnabled(false);
			fSelectionList.setEnabled(false);
		} else {
			verifyCurrentSelection();
			fSelectionList.selectFilterText();
			fSelectionList.setFocus();
		}
	}

	/**
	 * Returns the currently used filter text.
	 */
	protected String getFilter() {
		return fSelectionList.getFilter();
	}

	/**
	 * Returns the selection indices.
	 */
	protected int[] getSelectionIndices() {
		return fSelectionList.getSelectionIndices();
	}

	/**
	 * Returns the widget selection. Returns empty list when the widget is not
	 * usable.
	 */
	protected List<Object> getWidgetSelection() {
		if (fSelectionList == null || fSelectionList.isDisposed())
			return new ArrayList<>(0);
		return fSelectionList.getSelection();
	}

	/**
	 * An element as been selected in the list by double clicking on it.
	 * Emulate a OK button pressed to close the dialog.
	 */
	protected abstract void handleDoubleClick();

	/**
	 * Checks whether the list of elements is empty or not.
	 */
	protected boolean isEmptyList() {
		if (fSelectionList == null)
			return true;
		return fSelectionList.isEmptyList();
	}

	/**
	 * Constructs a list selection dialog.
	 * @param renderer The label renderer used
	 * @param ignoreCase Decides if the match string ignores lower/upppr case
	 * @param multipleSelection Allow multiple selection
	 */
	protected AbstractElementListSelectionDialog(Shell parent, String title, Image image, ILabelProvider renderer,
			boolean ignoreCase, boolean multipleSelection) {
		super(parent);
		setTitle(title);
		setImage(image);
		fRenderer = renderer;
		fIgnoreCase = ignoreCase;
		fIsMultipleSelection = multipleSelection;

		fMessageText = ""; //$NON-NLS-1$

		fCurrStatus = new StatusInfo();

		fValidator = null;
		fEmptyListMessage = ""; //$NON-NLS-1$
		fNothingSelectedMessage = ""; //$NON-NLS-1$
	}

	/**
	 * Constructs a list selection dialog.
	 * @param renderer The label renderer used
	 * @param ignoreCase Decides if the match string ignores lower/upppr case
	 * @param multipleSelection Allow multiple selection
	 */
	protected AbstractElementListSelectionDialog(Shell parent, ILabelProvider renderer, boolean ignoreCase,
			boolean multipleSelection) {
		this(parent, "", null, renderer, ignoreCase, multipleSelection); //$NON-NLS-1$
	}

	@Override
	public int open() {
		BusyIndicator.showWhile(null, () -> access$superOpen());
		return getReturnCode();
	}

	/**
	 * Refilters the current list according to the filter entered into the
	 * text edit field.
	 */
	protected void refilter() {
		fSelectionList.filter(true);
	}

	/**
	 * If a empty-list message is set, a error message is shown
	 * Must be set before widget creation
	 */
	public void setEmptyListMessage(String message) {
		fEmptyListMessage = message;
	}

	/**
	 * Sets the filter text to the given value.
	 */
	protected void setFilter(String text, boolean refilter) {
		fSelectionList.setFilter(text, refilter);
	}

	/**
	 * Sets the message to be shown above the match text field.
	 * Must be set before widget creation
	 */
	@Override
	public void setMessage(String message) {
		fMessageText = message;
	}

	/**
	 * If the selection is empty, this message is shown
	 */
	public void setNothingSelectedMessage(String message) {
		fNothingSelectedMessage = message;
	}

	/**
	 * Selects the elements in the list determined by the given
	 * selection indices.
	 */
	protected void setSelection(int[] selection) {
		fSelectionList.setSelection(selection);
	}

	/**
	 * Initializes the selection list widget with the given list of
	 * elements.
	 */
	protected void setSelectionListElements(List<Object> elements, boolean refilter) {
		fSelectionList.setElements(elements, refilter);
	}

	/**
	 * A validator can be set to check if the current selection
	 * is valid
	 */
	public void setValidator(ISelectionValidator validator) {
		fValidator = validator;
	}

	/**
	 * Verifies the current selection and updates the status line
	 * accordingly.
	 */
	protected boolean verifyCurrentSelection() {
		List<Object> sel = getWidgetSelection();
		int length = sel.size();
		if (length > 0) {
			if (fValidator != null) {
				fValidator.isValid(sel.toArray(), fCurrStatus);
			} else {
				fCurrStatus.setOK();
			}
		} else {
			if (isEmptyList()) {
				fCurrStatus.setError(fEmptyListMessage);
			} else {
				fCurrStatus.setError(fNothingSelectedMessage);
			}
		}
		updateStatus(fCurrStatus);
		return fCurrStatus.isOK();
	}
}
