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

import java.util.Calendar;
import java.util.Date;

import org.eclipse.core.databinding.observable.DisposeEvent;
import org.eclipse.core.databinding.observable.IDisposeListener;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.jface.action.Action;

import com.patrikdufresne.util.Localized;
import com.planimod.ui.theme.Resources;

/**
 * Action to change the planner period.
 * 
 * @author Patrik Dufresne
 * 
 */
public class ChangeDateAction extends Action implements IValueChangeListener,
		IDisposeListener {
	/**
	 * Next style action.
	 */
	private static final int STYLE_NEXT = 2;
	/**
	 * Previous style action.
	 */
	private static final int STYLE_PREVIOUS = 1;
	/**
	 * Today style action.
	 */
	private static final int STYLE_TODAY = 3;

	/**
	 * Property name of an action's unit state (value <code>"unit"</code>).
	 */
	public static final String UNIT = "unit"; //$NON-NLS-1$
	
	/**
	 * Unit to jump to next day.
	 */
	public static final int UNIT_DAY = 1;

	/**
	 * Unit to jump to next month.
	 */
	public static final int UNIT_MONTH = 2;

	/**
	 * Unit to jump to next week.
	 */
	public static final int UNIT_WEEK = 3;

	/**
	 * Create a new action to go foward in time.
	 * 
	 * @return
	 */
	public static ChangeDateAction nextAction() {
		ChangeDateAction action = new ChangeDateAction(STYLE_NEXT);
		action.setText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.next.text")); //$NON-NLS-1$
		action.setToolTipText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.next.toolTipText")); //$NON-NLS-1$
		action.setImageDescriptor(Resources
				.getImageDescriptor(Resources.ICON_GO_NEXT_16));
		action.setUnit(UNIT_DAY);
		return action;
	}

	/**
	 * Create a new action to go back in time.
	 * 
	 * @return
	 */
	public static ChangeDateAction previousAction() {
		ChangeDateAction action = new ChangeDateAction(STYLE_PREVIOUS);
		action.setText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.previous.text")); //$NON-NLS-1$
		action.setToolTipText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.previous.toolTipText")); //$NON-NLS-1$
		action.setImageDescriptor(Resources
				.getImageDescriptor(Resources.ICON_GO_PREVIOUS_16));
		action.setUnit(UNIT_DAY);
		return action;
	}

	/**
	 * Create an action to go at the current date.
	 * 
	 * @return
	 */
	public static ChangeDateAction todayAction() {
		ChangeDateAction action = new ChangeDateAction(STYLE_TODAY);
		action.setText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.next.text")); //$NON-NLS-1$
		action.setToolTipText(Localized.get(ChangeDateAction.class,
				"ChangeDateAction.next.toolTipText")); //$NON-NLS-1$
		action.setImageDescriptor(Resources
				.getImageDescriptor(Resources.ICON_GO_HOME_16));
		return action;
	}

	/**
	 * Define the date state to update.
	 */
	private IObservableValue date;

	/**
	 * The style of this action. One of the STYLE_* constants.
	 */
	private int style;

	/**
	 * Define the unit of the next date.
	 */
	private int unit;

	/**
	 * Create a new action.
	 * 
	 * @param planner
	 *            the planner to look for.
	 */
	public ChangeDateAction(int style) {
		this.style = style;
		setEnabled(canRun());
	}

	/**
	 * Returns True if the action can be run.
	 * 
	 * @return
	 */
	protected boolean canRun() {
		return this.date != null && !this.date.isDisposed()
				&& this.date.getValue() instanceof Date;
	}

	/**
	 * Returns the current date state of null if not define.
	 * 
	 * @return the date state or null.
	 */
	public IObservableValue getDate() {
		return this.date;
	}

	/**
	 * Return the unit of the jump.
	 * 
	 * @return the unit
	 */
	public int getUnit() {
		return this.unit;
	}

	@Override
	public void handleDispose(DisposeEvent event) {
		setEnabled(canRun());
	}

	@Override
	public void handleValueChange(ValueChangeEvent event) {
		setEnabled(canRun());
	}

	/**
	 * This implementation change the planner periode.
	 */
	@Override
	public void run() {
		// Get the date value from the state
		if (this.date == null || !(this.date.getValue() instanceof Date)) {
			return;
		}
		Date value = (Date) this.date.getValue();

		// Check the field to use
		int field;
		switch (this.unit) {
		case UNIT_WEEK:
			field = Calendar.WEEK_OF_YEAR;
			break;
		case UNIT_MONTH:
			field = Calendar.MONTH;
			break;
		default:
			field = Calendar.DATE;
		}

		Calendar cal = Calendar.getInstance();
		cal.setTime(value);
		switch (this.style) {
		case STYLE_PREVIOUS:
			cal.add(field, -1);
			this.date.setValue(cal.getTime());
			break;
		case STYLE_NEXT:
			cal.add(field, 1);
			this.date.setValue(cal.getTime());
			break;
		case STYLE_TODAY:
			this.date.setValue(new Date());
			break;
		}
	}

	/**
	 * Sets the date state to be updated.
	 * 
	 * @param date
	 *            the state.
	 */
	public void setDate(IObservableValue date) {
		// Check if the date state changed
		if (this.date == date) {
			return;
		}
		// Release previous listener
		if (this.date != null) {
			this.date.removeDisposeListener(this);
			this.date.removeValueChangeListener(this);
		}
		// Add a listener
		this.date = date;
		if (this.date != null) {
			this.date.addDisposeListener(this);
			this.date.addValueChangeListener(this);
		}
		setEnabled(canRun());
	}

	/**
	 * Sets the jump unit. One of the UNIT_ constants.
	 * 
	 * @param unit
	 *            the new unit value
	 */
	public void setUnit(int unit) {
		this.unit = unit;
		if (unit != this.unit) {
			Integer oldVal = Integer.valueOf(this.unit);
			Integer newVal = Integer.valueOf(unit);
			this.unit = unit;
			firePropertyChange(UNIT, oldVal, newVal);
		}
	}
}
