/*
 * Copyright (c) 2011, Patrik Dufresne. All rights reserved.
 * Patrik Dufresne PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.planimod.ui.views.employees;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.BindingProperties;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.ObservablesManager;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.core.databinding.validation.MultiValidator;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.dialog.TitleAreaDialogSupport;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
import org.eclipse.jface.databinding.viewers.ViewerSupport;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.TitleAreaDialog;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;

import com.patrikdufresne.jface.databinding.viewers.PropertyViewerComparator;
import com.patrikdufresne.jface.databinding.viewers.TextProposalViewerUpdater;
import com.patrikdufresne.jface.dialogs.DefaultValidationMessageProvider;
import com.patrikdufresne.jface.viewers.TextProposalViewer;
import com.patrikdufresne.util.Localized;
import com.planimod.core.PlanimodManagers;
import com.planimod.core.Position;
import com.planimod.core.Team;
import com.planimod.core.comparators.TeamComparators;
import com.planimod.ui.databinding.Converters;
import com.planimod.ui.databinding.manager.PlanimodObservables;
import com.planimod.ui.theme.Resources;

/**
 * This dialog is used to select the preferred position.
 * 
 * @author Patrik Dufresne
 * 
 */
public class PreferredPositionDialog extends TitleAreaDialog {

	public static final int CLEAR_ID = 3;

	/**
	 * Used for alocalization.
	 */
	protected Localized localized = Localized
			.load(PreferredPositionDialog.class);

	/**
	 * Manager to use.
	 */
	private PlanimodManagers managers;

	/**
	 * The selected position value.
	 */
	protected WritableValue position;

	/**
	 * Viewer to select the preferred position
	 */
	private TextProposalViewer positionViewer;

	/**
	 * The selected shifts value.
	 */
	protected WritableValue shift;

	/**
	 * Viewer to select the shifts for the preferred position.
	 */
	private ComboViewer teamViewer;

	private DataBindingContext dbc;

	private ObservablesManager om;

	/**
	 * Create a new dialog.
	 * 
	 * @param parentShell
	 *            the parent shell
	 */
	public PreferredPositionDialog(Shell parentShell, PlanimodManagers managers) {
		super(parentShell);
		this.managers = managers;
		this.shift = new WritableValue(null, Team.class);
		this.position = new WritableValue(null, Position.class);
	}

	/**
	 * This implementation sets the binding.
	 */
	@Override
	protected Control createContents(Composite parent) {
		Control control = super.createContents(parent);
		/*
		 * Bind widgets
		 */
		this.dbc = new DataBindingContext();
		this.om = new ObservablesManager();
		this.om.runAndCollect(new Runnable() {
			@Override
			public void run() {
				bindValues();
			}
		});
		this.om.addObservablesFromContext(this.dbc, true, true);

		return control;
	}

	/**
	 * Used to bind the values to the widget.
	 */
	protected void bindValues() {
		/*
		 * Bind the shift viewer
		 */
		IObservableSet teams = PlanimodObservables.listTeam(this.managers);
		IValueProperty teamLabel = BeanProperties.value(Team.class, Team.NAME)
				.value(BindingProperties.convertedValue(Converters
						.removeFrontNumber()));
		this.teamViewer.setComparator(new PropertyViewerComparator(
				TeamComparators.byName()));
		ViewerSupport.bind(this.teamViewer, teams, teamLabel);
		this.dbc.bindValue(
				ViewersObservables.observeSingleSelection(this.teamViewer),
				this.shift, null, null);

		/*
		 * Bind the position viewer
		 */
		IObservableSet classifiedPositions = new ClassifiedPositionComputedSet(
				this.managers);
		IValueProperty positionLabel = BeanProperties.value(Position.class,
				Position.NAME);
		this.positionViewer
				.setContentProvider(new ObservableSetContentProvider(
						new TextProposalViewerUpdater(this.positionViewer)));
		this.positionViewer.setLabelProvider(new ObservableMapLabelProvider(
				positionLabel.observeDetail(classifiedPositions)));
		this.positionViewer.setInput(classifiedPositions);

		this.dbc.bindValue(
				ViewersObservables.observeSingleSelection(this.positionViewer),
				this.position, null, null);

		// Create the multi-validator for start and end time
		MultiValidator validator = new MultiValidator() {
			@Override
			protected IStatus validate() {
				// Calculate the validation status for preferred position.
				Team shift = (Team) PreferredPositionDialog.this.shift
						.getValue();
				Position position = (Position) PreferredPositionDialog.this.position
						.getValue();
				if (shift == null && position == null) {
					return ValidationStatus
							.info(PreferredPositionDialog.this.localized
									.get("PreferredPositionDialog.message.preferredPositionRemoved")); //$NON-NLS-1$
				} else if (!(shift != null && position != null)
						&& (shift != null || position != null)) {
					return ValidationStatus
							.error(PreferredPositionDialog.this.localized
									.get("PreferredPositionDialog.message.selectShiftAndPosition")); //$NON-NLS-1$
				}

				return ValidationStatus.ok();
			}
		};
		this.dbc.addValidationStatusProvider(validator);

		// Bind the title area to the binding context
		TitleAreaDialogSupport
				.create(this, this.dbc)
				.setValidationMessageProvider(
						new DefaultValidationMessageProvider(
								this.localized
										.get("PreferredPositionDialog.titleArea.message"))); //$NON-NLS-1$

		// Bind the status to the OK button
		UpdateValueStrategy modelToTarget = new UpdateValueStrategy();
		modelToTarget.setConverter(new Converter(IStatus.class, Boolean.TYPE) {
			@Override
			public Object convert(Object fromObject) {
				return Boolean.valueOf(((IStatus) fromObject).getSeverity() == IStatus.OK);
			}
		});
		UpdateValueStrategy targetToModel = new UpdateValueStrategy();
		targetToModel.setConverter(new Converter(Boolean.TYPE, IStatus.class) {
			@Override
			public Object convert(Object fromObject) {
				return ((Boolean) fromObject).booleanValue() ? ValidationStatus
						.ok() : ValidationStatus.error(""); //$NON-NLS-1$
			}
		});
		this.dbc.bindValue(SWTObservables.observeEnabled(this.getButton(OK)),
				new AggregateValidationStatus(this.dbc.getBindings(),
						AggregateValidationStatus.MAX_SEVERITY), targetToModel,
				modelToTarget);
	}

	/**
	 * This implementation create the required widget to select the preferred
	 * position for an employee.
	 */
	@Override
	protected Control createDialogArea(Composite parent) {
		Composite comp = (Composite) super.createDialogArea(parent);

		Composite composite = new Composite(comp, SWT.NONE);
		composite.setLayout(new GridLayout(2, false));
		composite
				.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, true, true));

		// Setup the title area
		getShell().setText(this.localized.get("PreferredPositionDialog.title"));//$NON-NLS-1$
		setTitle(this.localized.get("PreferredPositionDialog.titleArea.title")); //$NON-NLS-1$
		setTitleImage(Resources.getImage(Resources.DLG_EDIT_TITLE_IMAGE));

		// Shift selection
		Label label = new Label(composite, SWT.NONE);
		label.setText(this.localized.get("PreferredPositionDialog.label.shift")); //$NON-NLS-1$
		this.teamViewer = new ComboViewer(composite);
		this.teamViewer.getControl().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, false));

		// Preferred position
		label = new Label(composite, SWT.NONE);
		label.setText(this.localized
				.get("PreferredPositionDialog.label.position")); //$NON-NLS-1$
		this.positionViewer = new TextProposalViewer(composite);
		this.positionViewer.getControl().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, false));

		// Build the separator line
		Label separator = new Label(comp, SWT.HORIZONTAL | SWT.SEPARATOR);
		separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		return comp;
	}

	/**
	 * Returns the current position.
	 * 
	 * @return
	 */
	public Position getPosition() {
		return (Position) this.position.getValue();
	}

	/**
	 * Returns the selected shift.
	 * 
	 * @return a shift or null
	 */
	public Team getShift() {
		return (Team) this.shift.getValue();
	}

	/**
	 * Sets the selected position.
	 * 
	 * @param position
	 *            a position or null.
	 */
	public void setPosition(Position position) {
		this.position.setValue(position);
	}

	/**
	 * Sets the selected shift.
	 * 
	 * @param shift
	 *            a shift or null
	 */
	public void setShift(Team shift) {
		this.shift.setValue(shift);
	}

	@Override
	protected void createButtonsForButtonBar(Composite parent) {
		// create OK and Cancel buttons by default
		createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL,
				true);
		createButton(parent, IDialogConstants.CANCEL_ID,
				IDialogConstants.CANCEL_LABEL, false);
		createButton(parent, PreferredPositionDialog.CLEAR_ID,
				this.localized.get("PreferredPositionDialog.clear"), false); //$NON-NLS-1$
	}

	@Override
	protected void buttonPressed(int buttonId) {
		if (buttonId == CLEAR_ID) {
			clearPressed();
		} else {
			super.buttonPressed(buttonId);
		}

	}

	protected void clearPressed() {
		setReturnCode(CLEAR_ID);
		close();
	}

}
