package de.afbb.bibo.databinding;

import java.util.HashMap;

import org.eclipse.core.databinding.AggregateValidationStatus;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.UpdateValueStrategy;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.observable.ChangeEvent;
import org.eclipse.core.databinding.observable.IChangeListener;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.swt.ISWTObservableValue;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;

import de.afbb.bibo.ui.observable.value.NotEmptyValue;

/**
 * utility class to hold common binding methods
 * 
 * @author dbecker
 */
public final class BindingHelper {

	private static final HashMap<Control, ControlDecoration> decorations = new HashMap<Control, ControlDecoration>();

	private BindingHelper() {
	}

	/**
	 * creates a bidirectional binding for a text field.<br>
	 * changes in model will be instantly reflected by the target and vice versa.
	 * 
	 * @param textField
	 *            widget for input
	 * @param entity
	 *            class that holds the model informations
	 * @param entityClass
	 *            class of entity
	 * @param propertyName
	 *            name of the property that should be binded. expected to be of {@link String} type
	 * @param bindingContext
	 *            context of the binding
	 * @return binding
	 */
	public static Binding bindStringToTextField(final Text textField, final Object entity, final Class<?> entityClass,
			final String propertyName, final DataBindingContext bindingContext, final boolean required) {
		final ISWTObservableValue targetObservable = SWTObservables.observeText(textField, SWT.Modify);
		final IObservableValue modelObservable = BeanProperties.value(entityClass, propertyName).observe(entity);
		final UpdateValueStrategy targetToModel = required ? new UpdateValueStrategy().setAfterConvertValidator(new NotEmptyValue()) : null;

		final Binding binding = bindingContext.bindValue(targetObservable, modelObservable, targetToModel, null);

		if (required) {
			createControlDecoration(textField, binding, NotEmptyValue.MSG);
		}

		return binding;
	}

	public static ControlDecoration createControlDecoration(final Control control, final Binding binding, final String message) {
		final ControlDecoration controlDecoration = new ControlDecoration(control, SWT.RIGHT | SWT.TOP);
		final FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR);
		controlDecoration.setImage(fieldDecoration.getImage());
		decorations.put(control, controlDecoration);
		control.addDisposeListener(new DisposeListener() {

			@Override
			public void widgetDisposed(final DisposeEvent e) {
				final ControlDecoration remove = decorations.remove(control);
				remove.dispose();
			}

		});
		return controlDecoration;
	}

	/**
	 * aggregates the validation status for given {@link DataBindingContext}
	 * 
	 * @param bindingContext
	 * @return
	 */
	public static AggregateValidationStatus aggregateValidationStatus(final DataBindingContext bindingContext) {
		final AggregateValidationStatus aggregateStatus = new AggregateValidationStatus(bindingContext,
				AggregateValidationStatus.MAX_SEVERITY);
		aggregateStatus.addChangeListener(new IChangeListener() {

			@Override
			public void handleChange(final ChangeEvent event) {
				for (final Object o : bindingContext.getBindings()) {
					final Binding binding = (Binding) o;
					final IStatus status = (IStatus) binding.getValidationStatus().getValue();

					Control control = null;
					if (binding.getTarget() instanceof ISWTObservableValue) {
						final ISWTObservableValue swtObservableValue = (ISWTObservableValue) binding.getTarget();
						control = (Control) swtObservableValue.getWidget();
					}
					final ControlDecoration decoration = decorations.get(control);
					if (decoration != null) {
						if (status.isOK()) {
							decoration.hide();
						} else {
							decoration.setDescriptionText(status.getMessage());
							decoration.show();
						}
					}
				}
			}
		});
		return aggregateStatus;

	}

}

@@ -2722,20 +2722,30 @@
 alue.MSG
+, required
 );%0D%0A
-
 %09%09%7D%0D%0A%0D%0A%09
@@ -2891,16 +2891,44 @@
  message
+,%0D%0A%09%09%09final boolean required
 ) %7B%0D%0A%09%09f
@@ -3021,11 +3021,14 @@
 SWT.
+BOT
 TO
-P
+M
 );%0D%0A
@@ -3492,23 +3492,553 @@
 %09%09%09%7D%0D%0A%0D%0A
-
 %09%09%7D);%0D%0A
+%09%09if (required) %7B%0D%0A%09%09%09final ControlDecoration requiredDecoration = new ControlDecoration(control, SWT.RIGHT %7C SWT.TOP);%0D%0A%09%09%09final FieldDecoration requiredFieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(%0D%0A%09%09%09%09%09FieldDecorationRegistry.DEC_REQUIRED);%0D%0A%09%09%09requiredDecoration.setImage(requiredFieldDecoration.getImage());%0D%0A%09%09%09control.addDisposeListener(new DisposeListener() %7B%0D%0A%0D%0A%09%09%09%09@Override%0D%0A%09%09%09%09public void widgetDisposed(final DisposeEvent e) %7B%0D%0A%09%09%09%09%09requiredDecoration.dispose();%0D%0A%09%09%09%09%7D%0D%0A%0D%0A%09%09%09%7D);%0D%0A%09%09%7D%0D%0A
 %09%09return

add required decoration