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

import java.util.Date;

import org.eclipse.core.databinding.BindingProperties;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.conversion.Converter;
import org.eclipse.core.databinding.conversion.IConverter;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.map.IObservableMap;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.jface.action.ActionContributionItem;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.action.Separator;
import org.eclipse.jface.databinding.util.JFaceProperties;
import org.eclipse.jface.databinding.viewers.ObservableSetContentProvider;
import org.eclipse.jface.databinding.viewers.ViewersObservables;
import org.eclipse.jface.viewers.IOpenListener;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;

import com.patrikdufresne.jface.databinding.preference.PreferenceStoreProperties;
import com.patrikdufresne.jface.databinding.viewers.PropertyViewerComparator;
import com.patrikdufresne.planner.Planner;
import com.patrikdufresne.planner.databinding.PlannerProperties;
import com.patrikdufresne.planner.databinding.PlannerViewerUpdater;
import com.patrikdufresne.planner.viewer.PlannerViewer;
import com.patrikdufresne.util.Localized;
import com.planimod.core.AbstractCalendarEvent;
import com.planimod.core.PlanimodManagers;
import com.planimod.core.Shift;
import com.planimod.core.Team;
import com.planimod.core.comparators.ShiftComparators;
import com.planimod.ui.databinding.Converters;
import com.planimod.ui.databinding.DateColorControlStyleSupport;
import com.planimod.ui.databinding.ListObservableValue;
import com.planimod.ui.databinding.manager.ShiftComputedSet;
import com.planimod.ui.preference.PlanimodPreferences;
import com.planimod.ui.viewers.planner.ObservableMapPlannerLabelProviderWithColor;
import com.planimod.ui.viewers.planner.PlanimodPlannerLook;
import com.planimod.ui.views.AbstractViewPart;
import com.planimod.ui.views.ChangeDateAction;
import com.planimod.ui.views.RemoveAction;

/**
 * This view part display shifts information in a planner view.
 * 
 * @author Patrik Dufresne
 * 
 */
public class ShiftViewPart extends AbstractViewPart {
	private static final IConverter CONVERTER = new Converter(Shift.class,
			String.class) {
		@Override
		public Object convert(Object fromObject) {
			return ((Shift) fromObject).getTeam().getName();
		}
	};
	private static final String DOT = "."; //$NON-NLS-1$
	/**
	 * The part id.
	 */
	public static final String ID = "com.planimod.ui.views.shifts.ShiftViewPart"; //$NON-NLS-1$
	/**
	 * Add action.
	 */
	private AddShiftAction addShiftAction;
	/**
	 * Edit action.
	 */
	protected EditShiftAction editShiftAction;
	/**
	 * Used for localization.
	 */
	private Localized localized = Localized.load(ShiftViewPart.class);
	private ChangeDateAction nextAction;
	private ChangeDateAction previousAction;
	private RemoveAction removeAction;
	private RemoveAllShiftAction removeAllAction;

	private ChangeDateAction todayAction;

	private PlannerViewer viewer;

	/**
	 * Create a new view part.
	 */
	public ShiftViewPart() {
		super(ID);
		setTitle(this.localized.get("ShiftViewPart.title")); //$NON-NLS-1$
	}

	@Override
	public void activate(Composite parent) {

		// Create the planner viewer
		this.viewer = createPlannerViewer(parent);
		this.viewer.getControl().setLayoutData(
				new GridData(SWT.FILL, SWT.FILL, true, true));

		// Populate the tool bar.
		fillToolBar();

		// Create binding
		bind();

	}

	/**
	 * Create data binding.
	 */
	@Override
	protected void bindValues() {
		/*
		 * Get the states.
		 */
		WritableValue date = new WritableValue(null, Date.class);
		getDbc().bindValue(
				date,
				PreferenceStoreProperties.value(PlanimodPreferences.DATE,
						String.class).observe(
						PlanimodPreferences.getPreferenceStore()));
		if (date.getValue() == null) {
			date.setValue(new Date());
		}

		/*
		 * Bind planner date selection
		 */
		Planner planner = this.viewer.getPlanner();
		getDbc().bindValue(PlannerProperties.dateSelection().observe(planner),
				date);
		IObservableValue start = PlannerProperties.startDateValue().observe(
				planner);
		IObservableValue end = PlannerProperties.endDateValue()
				.observe(planner);

		/*
		 * Bind planner viewer.
		 */
		IObservableSet shifts = new ShiftComputedSet(getSite().getService(
				PlanimodManagers.class), start, end);
		IObservableMap shiftLabels = BindingProperties.convertedValue(
				Converters.removeFrontNumber()).observeDetail(
				BeanProperties.value(Shift.class, Shift.TEAM + DOT + Team.NAME)
						.observeDetail(shifts));
		IObservableMap shiftStart = BeanProperties.value(
				AbstractCalendarEvent.class, AbstractCalendarEvent.START_DATE)
				.observeDetail(shifts);
		IObservableMap shiftEnd = BeanProperties.value(
				AbstractCalendarEvent.class, AbstractCalendarEvent.END_DATE)
				.observeDetail(shifts);
		IObservableMap color = BeanProperties
				.value(Shift.class, Shift.TEAM + DOT + Team.COLOR)
				.value(BindingProperties.convertedValue(Converters
						.StringToRGB())).observeDetail(shifts);
		this.viewer.setContentProvider(new ObservableSetContentProvider(
				new PlannerViewerUpdater(this.viewer)));
		this.viewer
				.setLabelProvider(new ObservableMapPlannerLabelProviderWithColor(
						shiftLabels, shiftStart, shiftEnd, color));
		this.viewer.setComparator(new PropertyViewerComparator(ShiftComparators
				.byHourAndTeam()));
		this.viewer.setInput(filter(shifts, CONVERTER));

		IObservableValue singleSelection = ViewersObservables
				.observeSingleSelection(this.viewer);
		IObservableList selection = ViewersObservables
				.observeMultiSelection(this.viewer);

		/*
		 * Bind the planner background color.
		 */
		DateColorControlStyleSupport.create(this.viewer.getPlanner(), date);

		/*
		 * Bind actions.
		 */
		this.previousAction.setDate(date);
		this.previousAction.setUnit(ChangeDateAction.UNIT_WEEK);
		this.todayAction.setDate(date);
		this.nextAction.setDate(date);
		this.nextAction.setUnit(ChangeDateAction.UNIT_WEEK);

		// Add shift event action
		getDbc().bindValue(
				JFaceProperties.value(AddShiftAction.class,
						AddShiftAction.DATE, AddShiftAction.DATE).observe(
						this.addShiftAction), date);

		// Edit action
		getDbc().bindValue(
				JFaceProperties.value(EditShiftAction.class,
						EditShiftAction.SHIFT, EditShiftAction.SHIFT).observe(
						this.editShiftAction), singleSelection);

		// Remove action.
		getDbc().bindValue(
				JFaceProperties.value(RemoveAction.class, RemoveAction.OBJECTS,
						RemoveAction.OBJECTS).observe(this.removeAction),
				new ListObservableValue(selection, AbstractCalendarEvent.class));

		// Remove all action
		getDbc().bindValue(
				JFaceProperties.value(RemoveAllShiftAction.class,
						RemoveAllShiftAction.DATE, RemoveAllShiftAction.DATE)
						.observe(this.removeAllAction), date);

	}

	/**
	 * This implementation create a planner viewer.
	 */
	protected PlannerViewer createPlannerViewer(Composite parent) {

		PlannerViewer viewer = new PlannerViewer(parent, SWT.BORDER
				| SWT.H_SCROLL | SWT.V_SCROLL | SWT.WRAP);

		// Set widget properties
		viewer.getPlanner().setLook(new PlanimodPlannerLook());

		viewer.addOpenListener(new IOpenListener() {
			@Override
			public void open(OpenEvent event) {
				ShiftViewPart.this.editShiftAction.run();
			}
		});

		return viewer;

	}

	/**
	 * This implementation free allocated resources.
	 */
	@Override
	public void deactivate() {
		try {
			this.addShiftAction = null;
			this.editShiftAction = null;
			this.nextAction = null;
			this.previousAction = null;
			this.removeAction = null;
			this.todayAction = null;
		} finally {
			super.deactivate();
		}
	}

	@Override
	public void dispose() {
		this.localized = null;
		super.dispose();
	}

	/**
	 * Populate the tool bar.
	 */
	void fillToolBar() {

		IToolBarManager toolbar = getSite().getToolBarManager();
		/*
		 * Date navigation.
		 */
		// Previous
		toolbar.add(this.previousAction = ChangeDateAction.previousAction());
		// Today
		toolbar.add(this.todayAction = ChangeDateAction.todayAction());
		// Next
		toolbar.add(this.nextAction = ChangeDateAction.nextAction());

		/*
		 * Add action.
		 */
		toolbar.add(new Separator());

		// Add
		ActionContributionItem aci;
		toolbar.add(aci = new ActionContributionItem(
				this.addShiftAction = new AddShiftAction(getSite().getService(
						PlanimodManagers.class), getSite())));
		aci.setMode(ActionContributionItem.MODE_FORCE_TEXT);

		// Edit
		toolbar.add(aci = new ActionContributionItem(
				this.editShiftAction = new EditShiftAction(getSite()
						.getService(PlanimodManagers.class), getSite())));
		aci.setMode(ActionContributionItem.MODE_FORCE_TEXT);

		// Remove
		toolbar.add(aci = new ActionContributionItem(
				this.removeAction = new RemoveAction(getSite().getService(
						PlanimodManagers.class), getSite())));
		this.removeAction.setConfirmationMessage(this.localized
				.get("ShiftViewPart.removeAction.confirmationMessage")); //$NON-NLS-1$
		this.removeAction.setConfirmationShortDetail(this.localized
				.get("ShiftViewPart.removeAction.confirmationShortDetail")); //$NON-NLS-1$
		aci.setMode(ActionContributionItem.MODE_FORCE_TEXT);

		// Remove all
		toolbar.add(aci = new ActionContributionItem(
				this.removeAllAction = new RemoveAllShiftAction(getSite()
						.getService(PlanimodManagers.class), getSite(),
						getSite().getMainWindow())));
		aci.setMode(ActionContributionItem.MODE_FORCE_TEXT);

		// Text filter
		fillToolbarWithTextFilter();
	}

}
