package semorg.gui.util;

import java.util.Vector;

import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Spinner;
import org.eclipse.swt.widgets.TableColumn;

/** This class is used */
public class ConfigureListWidget extends Composite {

    /** The class provides the labels for GUI table columns. */
    class TableColumnPropertyLabelProvider extends LabelProvider {
	/**
	 * Casts a given element to {@link TableColumnProperty} and returns its
	 * text.
	 */
	public String getText(Object element) {
	    return ((TableColumnProperty) element).getText();
	}
    }

    /**
     * This class provides the content of the columns lists, i.e. column names,
     * their alignment and the width.
     */
    class TableColumnPropertyContentProvider implements
	    IStructuredContentProvider {

	private ListViewer listViewer;

	/** The input */
	private Vector<TableColumnProperty> input;

	/**
	 * Sets the list viewer {@link #listViewer} and its content
	 * {@link #input} to the given values.
	 * 
	 * @param viewer
	 *                the new viewer of this
	 *                {@link TableColumnPropertyContentProvider}
	 * @param newInput
	 *                the new input of this
	 *                {@link TableColumnPropertyContentProvider}
	 */
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
	    if (newInput != null) {
		this.listViewer = (ListViewer) viewer;
		this.input = (Vector<TableColumnProperty>) newInput;
	    }
	}

	/** Returns the {@link #input} as an array of objects. */
	public Object[] getElements(Object arg0) {
	    return input.toArray();
	}

	// not implemented
	public void dispose() {
	}

	/**
	 * Adds column to the input of this content provider.
	 * 
	 * @param column
	 *                the {@link TableColumnProperty} to add to the
	 *                {@link #input}.
	 */
	public void addColumn(TableColumnProperty column) {
	    input.add(column);
	    listViewer.add(column);
	}

	/**
	 * Removes a column from the input of this content provider.
	 * 
	 * @param column
	 *                the {@link TableColumnProperty} instance to remove
	 *                from the {@link #input}.
	 */
	public void removeColumn(TableColumnProperty column) {
	    input.remove(column);
	    listViewer.remove(column);
	}

	/**
	 * Removes all {@link TableColumnProperty} instances in the given vector
	 * from the input of this content provider.
	 * 
	 * @param columns
	 *                vector of columns to remove from the {@link #input}.
	 */
	public void removeAllColumns(Vector<TableColumnProperty> columns) {
	    input.removeAll(columns);
	    listViewer.refresh();
	}

	/**
	 * Adds all {@link TableColumnProperty} instance in the given vector to
	 * the input of this content provider.
	 * 
	 * @param columns
	 *                vector of columns to add to the {@link #input}.
	 */
	public void addAllColumns(Vector<TableColumnProperty> columns) {
	    input.addAll(columns);
	    listViewer.refresh();
	}

	/**
	 * @return the {@link #input}.
	 */
	public Vector<TableColumnProperty> getInput() {
	    return input;
	}

	/**
	 * Moves the given column up by one position in the {@link #input} of
	 * this content provider, i.e. the index gets decremented by one.
	 * 
	 * @param selectedColumn
	 *                column to move up by one postion
	 */
	public void moveUp(TableColumnProperty selectedColumn) {
	    int index = input.indexOf(selectedColumn);
	    input.remove(index);
	    input.add(index - 1, selectedColumn);
	    listViewer.refresh();
	}

	/**
	 * Moves the given column down by one position in the {@link #input} of
	 * this content provider, i.e. the index gets incremented by one.
	 * 
	 * @param selectedColumn
	 *                columns to move down by one position
	 */
	public void moveDown(TableColumnProperty selectedColumn) {
	    int index = input.indexOf(selectedColumn);
	    input.remove(index);
	    input.add(index + 1, selectedColumn);
	    listViewer.refresh();
	}

    }

    /** Array of table columns with unchanged order. */
    private TableColumnProperty[] columnProperties;

    /**
     * The list viewer which manages the list used to display the selected
     * columns.
     */
    private ListViewer selectedColumnsViewer;

    /**
     * The list viewer which manages the list used to display the deselected
     * columns.
     */
    private ListViewer deselectedColumnsViewer;

    /** The content provider for the {@link #selectedColumnsViewer}. */
    private TableColumnPropertyContentProvider selectedColumnsCP = new TableColumnPropertyContentProvider();

    /** The content provider for the {@link #deselectedColumnsViewer}. */
    private TableColumnPropertyContentProvider deselectedColumnsCP = new TableColumnPropertyContentProvider();

    /** The List widget which contains the selected columns. */
    private List selectedColumns;

    /** The List widget which contains the deselected columns. */
    private List deselectedColumns;

    /**
     * Creates a {@link ConfigureListDlg} instance with the given parameters.
     * 
     * @param parent
     *                the parent composite, which contains the
     *                {@link ConfigureListDlg}.
     * @param currentColumns
     *                array of the current columns.
     * @param columnOrder
     *                the order of columns.
     */
    public ConfigureListWidget(Composite parent, TableColumn[] currentColumns,
	    int[] columnOrder) {
	super(parent, SWT.NULL);
	setLayout(new GridLayout(4, false));

	Label deselectedColumnsLabel = new Label(this, SWT.NULL);
	deselectedColumnsLabel.setText(Messages
		.getString("GUIText.ConfigureListWidgetAvailableColumnsText"));
	GridData colLabelGD = new GridData();
	colLabelGD.horizontalSpan = 2;
	colLabelGD.horizontalAlignment = GridData.FILL;
	deselectedColumnsLabel.setLayoutData(colLabelGD);

	Label selectedColumnsLabel = new Label(this, SWT.NULL);
	selectedColumnsLabel.setText(Messages
		.getString("GUIText.ConfigureListWidgetSelectedColumnsText"));
	selectedColumnsLabel.setLayoutData(colLabelGD);

	this.columnProperties = new TableColumnProperty[currentColumns.length];
	for (int i = 0; i < currentColumns.length; i++) {
	    columnProperties[i] = new TableColumnProperty(i, columnOrder[i],
		    currentColumns[i].getText(), currentColumns[i]
			    .getAlignment(), currentColumns[i].getWidth(),
		    currentColumns[i].getResizable());
	}

	deselectedColumnsViewer = new ListViewer(this, SWT.V_SCROLL
		| SWT.BORDER | SWT.SINGLE);
	deselectedColumnsViewer
		.setLabelProvider(new TableColumnPropertyLabelProvider());
	deselectedColumnsViewer.setContentProvider(deselectedColumnsCP);
	deselectedColumns = deselectedColumnsViewer.getList();

	Composite moveButtonsArea = new Composite(this, SWT.NULL);

	selectedColumnsViewer = new ListViewer(this, SWT.V_SCROLL | SWT.BORDER
		| SWT.SINGLE);
	selectedColumnsViewer
		.setLabelProvider(new TableColumnPropertyLabelProvider());
	selectedColumnsViewer.setContentProvider(selectedColumnsCP);
	selectedColumns = selectedColumnsViewer.getList();

	Composite detailsArea = new Composite(this, SWT.NULL);

	final Button selectButton = new Button(moveButtonsArea, SWT.PUSH);
	final Button deselectButton = new Button(moveButtonsArea, SWT.PUSH);
	final Button selectAllButton = new Button(moveButtonsArea, SWT.PUSH);
	final Button deselectAllButton = new Button(moveButtonsArea, SWT.PUSH);

	Composite widthComp = new Composite(detailsArea, SWT.NULL);
	Label widthLabel = new Label(widthComp, SWT.NULL);
	widthLabel.setText(Messages
		.getString("GUIText.ConfigureListWidgetWidthText"));
	RowLayout widthCompLayout = new RowLayout(SWT.VERTICAL);
	widthCompLayout.marginHeight = 0;
	widthCompLayout.marginLeft = 0;
	widthCompLayout.marginRight = 0;
	widthCompLayout.marginWidth = 0;
	widthCompLayout.spacing = 0;
	widthCompLayout.fill = true;
	widthComp.setLayout(widthCompLayout);

	final Spinner widthSpinner = new Spinner(widthComp, SWT.BORDER);

	Composite orientationComp = new Composite(detailsArea, SWT.NULL);
	Label orientationLabel = new Label(orientationComp, SWT.NULL);
	orientationLabel.setText(Messages
		.getString("GUIText.ConfigureListWidgetOrientationText"));
	orientationComp.setLayout(widthCompLayout);

	final Combo orientationCombo = new Combo(orientationComp, SWT.BORDER
		| SWT.READ_ONLY);
	final Button higherButton = new Button(detailsArea, SWT.PUSH);
	final Button lowerButton = new Button(detailsArea, SWT.PUSH);

	deselectedColumnsViewer
		.addSelectionChangedListener(new ISelectionChangedListener() {
		    public void selectionChanged(SelectionChangedEvent evt) {
			boolean isSthSelected = !(evt.getSelection().isEmpty());
			selectButton.setEnabled(isSthSelected);
		    }
		});

	deselectedColumnsViewer
		.addDoubleClickListener(new IDoubleClickListener() {

		    public void doubleClick(DoubleClickEvent evt) {
			onSelect();
		    }
		});

	selectedColumnsViewer
		.addSelectionChangedListener(new ISelectionChangedListener() {
		    public void selectionChanged(SelectionChangedEvent evt) {
			boolean isSthSelected = !(evt.getSelection().isEmpty());
			deselectButton.setEnabled(isSthSelected);
			higherButton.setEnabled(isSthSelected
				&& selectedColumnsViewer.getList()
					.getSelectionIndex() != 0);
			lowerButton
				.setEnabled(isSthSelected
					&& selectedColumnsViewer.getList()
						.getSelectionIndex() != selectedColumnsViewer
						.getList().getItemCount() - 1);
			widthSpinner.setEnabled(isSthSelected);
			orientationCombo.setEnabled(isSthSelected);
			if (isSthSelected) {
			    TableColumnProperty column = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
				    .getSelection())).getFirstElement();
			    widthSpinner.setSelection(column.getWidth());
			    switch (column.getAlignment()) {
			    case SWT.LEFT:
				orientationCombo.select(0);
				break;
			    case SWT.RIGHT:
				orientationCombo.select(1);
				break;
			    case SWT.CENTER:
				orientationCombo.select(2);
				break;
			    default:
				orientationCombo.setText("");
			    }
			} else
			    widthSpinner.setSelection(0);
			orientationCombo.setText("");
		    }
		});

	selectedColumnsViewer
		.addDoubleClickListener(new IDoubleClickListener() {

		    public void doubleClick(DoubleClickEvent evt) {
			onDeselect();
		    }
		});

	RowLayout moveAreaLayout = new RowLayout(SWT.VERTICAL);
	moveAreaLayout.spacing = 10;
	moveButtonsArea.setLayout(moveAreaLayout);

	RowData buttonRowData = new RowData();
	buttonRowData.width = 50;

	selectButton.setText(">");
	selectButton.setEnabled(false);
	selectButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		onSelect();
	    }
	});

	selectButton.setLayoutData(buttonRowData);

	deselectButton.setText("<");
	deselectButton.setEnabled(false);
	deselectButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		onDeselect();
	    }

	});
	deselectButton.setLayoutData(buttonRowData);

	selectAllButton.setText(">>");
	selectAllButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		Vector<TableColumnProperty> elements = (Vector<TableColumnProperty>) deselectedColumnsViewer
			.getInput();
		for (int i = 0; i < elements.size(); i++) {
		    elements.get(i).setResizable(true);
		    elements.get(i).setWidth(100);
		}

		elements = new Vector(elements);
		deselectedColumnsCP.removeAllColumns(elements);
		selectedColumnsCP.addAllColumns(elements);
	    }
	});

	selectAllButton.setLayoutData(buttonRowData);

	deselectAllButton.setText("<<");
	deselectAllButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		Vector<TableColumnProperty> elements = (Vector<TableColumnProperty>) selectedColumnsViewer
			.getInput();
		for (int i = 0; i < elements.size(); i++) {
		    elements.get(i).setResizable(false);
		    elements.get(i).setWidth(0);
		}

		elements = new Vector(elements);
		selectedColumnsCP.removeAllColumns(elements);
		deselectedColumnsCP.addAllColumns(elements);
	    }
	});

	deselectAllButton.setLayoutData(buttonRowData);

	RowLayout detailsAreaLayout = new RowLayout(SWT.VERTICAL);
	detailsAreaLayout.spacing = 8;
	detailsAreaLayout.fill = true;
	detailsArea.setLayout(detailsAreaLayout);

	RowData detailsRowData = new RowData();
	detailsRowData.width = 80;

	RowData innerDetailsRowData = new RowData();
	innerDetailsRowData.width = 50;

	widthComp.setLayoutData(detailsRowData);

	widthSpinner.setLayoutData(innerDetailsRowData);
	widthSpinner.setEnabled(false);
	widthSpinner.setMinimum(0);
	widthSpinner.setMaximum(500);
	widthSpinner.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
			.getSelection())).getFirstElement();
		selectedColumn.setWidth(widthSpinner.getSelection());
	    }
	});

	orientationComp.setLayoutData(detailsRowData);

	orientationCombo.setLayoutData(innerDetailsRowData);
	orientationCombo.setEnabled(false);
	orientationCombo.add(Messages
		.getString("GUIText.ConfigureListWidgetOrientationLeftText"));
	orientationCombo.add(Messages
		.getString("GUIText.ConfigureListWidgetOrientationRightText"));
	orientationCombo.add(Messages
		.getString("GUIText.ConfigureListWidgetOrientationCenterText"));
	orientationCombo.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
			.getSelection())).getFirstElement();
		switch (orientationCombo.getSelectionIndex()) {
		case 0:
		    selectedColumn.setAlignment(SWT.LEFT);
		    break;
		case 1:
		    selectedColumn.setAlignment(SWT.RIGHT);
		    break;
		case 2:
		    selectedColumn.setAlignment(SWT.CENTER);
		    break;
		}
	    }
	});

	higherButton.setText(Messages
		.getString("GUIText.ConfigureListWidgetHigherButtonText"));
	higherButton.setEnabled(false);
	higherButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
			.getSelection())).getFirstElement();
		selectedColumnsCP.moveUp(selectedColumn);
		higherButton.setEnabled(selectedColumnsViewer.getList()
			.getSelectionIndex() != 0);
		lowerButton.setEnabled(selectedColumnsViewer.getList()
			.getSelectionIndex() != selectedColumnsViewer.getList()
			.getItemCount() - 1);
	    }
	});
	higherButton.setLayoutData(detailsRowData);

	lowerButton.setText(Messages
		.getString("GUIText.ConfigureListWidgetLowerButtonText"));
	lowerButton.setEnabled(false);
	lowerButton.addSelectionListener(new SelectionAdapter() {
	    public void widgetSelected(SelectionEvent evt) {
		TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
			.getSelection())).getFirstElement();
		selectedColumnsCP.moveDown(selectedColumn);
		higherButton.setEnabled(selectedColumnsViewer.getList()
			.getSelectionIndex() != 0);
		lowerButton.setEnabled(selectedColumnsViewer.getList()
			.getSelectionIndex() != selectedColumnsViewer.getList()
			.getItemCount() - 1);
	    }
	});
	lowerButton.setLayoutData(detailsRowData);

	GridData listGD = new GridData();
	listGD.widthHint = 150;
	listGD.heightHint = 150;

	deselectedColumns.setLayoutData(listGD);
	selectedColumns.setLayoutData(listGD);

	setListViewerInputs(columnProperties, columnOrder);
    }

    /**
     * Fills the both lists {@link #selectedColumns} and
     * {@link #deselectedColumns} with content using the
     * {@link #selectedColumnsViewer} and the {@link #deselectedColumnsViewer}.
     * 
     * @param columns
     *                the columns to manage
     * @param columnOrder
     *                the order of the columns
     */
    private void setListViewerInputs(TableColumnProperty[] columns,
	    int[] columnOrder) {
	Vector<TableColumnProperty> selectedColumnsVector = new Vector<TableColumnProperty>();
	Vector<TableColumnProperty> deselectedColumnsVector = new Vector<TableColumnProperty>();

	for (int i = 0; i < columns.length; i++) {
	    // for considering the order of the columns
	    TableColumnProperty currentColumn = columns[columnOrder[i]];

	    if (!currentColumn.getResizable() && currentColumn.getWidth() == 0) {
		// ==> width 0 and not resizable ==> deactivated column
		deselectedColumnsVector.add(currentColumn);

	    } else {
		selectedColumnsVector.add(currentColumn);
	    }
	}
	selectedColumnsViewer.setInput(selectedColumnsVector);
	deselectedColumnsViewer.setInput(deselectedColumnsVector);
    }

    /**
     * Sets the columns numbers subject to the order of colmuns displayed in the
     * lists of selected columns.
     */
    public void updateColumnNumbers() {
	int columnsDone = 0;

	for (int i = 0; i < selectedColumnsCP.input.size(); i++) {
	    selectedColumnsCP.input.get(i).setColumnNumber(columnsDone);
	    columnsDone++;
	}
	for (int i = 0; i < deselectedColumnsCP.input.size(); i++) {
	    deselectedColumnsCP.input.get(i).setColumnNumber(columnsDone);
	    columnsDone++;
	}
    }

    /**
     * @return An array of {@link TableColumnProperty} objects.
     */
    public TableColumnProperty[] getColumns() {
	updateColumnNumbers();
	return columnProperties;
    }

    /**
     * Moves a column from the list with deselected columns to the list of
     * selected columns. This method gets invoked by clicking the select button.
     */
    private void onSelect() {
	TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (deselectedColumnsViewer
		.getSelection())).getFirstElement();

	selectedColumn.setResizable(true);
	selectedColumn.setWidth(100);

	deselectedColumnsCP.removeColumn(selectedColumn);
	selectedColumnsCP.addColumn(selectedColumn);
    }

    /**
     * Moves a column from the list with selected columns to the list of
     * deselected columns. This method gets invoked by clicking the deselect
     * button.
     */
    private void onDeselect() {
	TableColumnProperty selectedColumn = (TableColumnProperty) ((StructuredSelection) (selectedColumnsViewer
		.getSelection())).getFirstElement();
	selectedColumn.setResizable(false);
	selectedColumn.setWidth(0);

	selectedColumnsCP.removeColumn(selectedColumn);
	deselectedColumnsCP.addColumn(selectedColumn);
    }
}
