package com.planimod.ui.views;

import java.util.Collection;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.window.IShellProvider;
import org.eclipse.swt.widgets.Display;

import com.patrikdufresne.managers.ManagedObject;
import com.patrikdufresne.managers.ManagerException;
import com.patrikdufresne.util.Localized;
import com.planimod.core.IntegrityException;
import com.planimod.core.PlanimodManagers;
import com.planimod.ui.MessageUtils;

/**
 * Action to add object using managers.
 * 
 * @author patapouf
 * 
 */
public abstract class AbstractAddAction extends Action {

	/**
	 * Managers to used to add the element.
	 */
	private PlanimodManagers managers;

	/**
	 * Shell provider.
	 */
	private IShellProvider shellProvider;
	/**
	 * Selection provider.
	 */
	private ISelectionProvider selectionProvider;

	/**
	 * Create a new Add object action.
	 * 
	 * @param provider
	 *            the selection provider
	 * @param shellProvider
	 *            the shell provider use to display message box
	 * @param factory
	 *            the factory
	 */
	public AbstractAddAction(PlanimodManagers managers,
			IShellProvider shellProvider) {
		this(managers, shellProvider, null);
	}

	/**
	 * Create a new Add object action.
	 * 
	 * @param provider
	 *            the selection provider
	 * @param shellProvider
	 *            the shell provider use to display message box
	 * @param factory
	 *            the factory
	 */
	public AbstractAddAction(PlanimodManagers managers,
			IShellProvider shellProvider, ISelectionProvider selectionProvider) {
		super(null);
		if (managers == null || shellProvider == null) {
			throw new NullPointerException();
		}
		this.managers = managers;
		this.shellProvider = shellProvider;
		this.selectionProvider = selectionProvider;
		setEnabled(canCreateObject());
	}

	/**
	 * Check if it's possible to create the object using the given selection
	 * context.
	 * 
	 * @param selection
	 *            the selection context
	 * @return True if it's possible to create an object
	 */
	protected abstract boolean canCreateObject();

	/**
	 * Create the object.
	 * 
	 * @param selection
	 *            the selection context.
	 * @return the new object to add using a manager.
	 */
	protected abstract List<? extends ManagedObject> createObjects()
			throws ManagerException;

	/**
	 * Returns the managers used by this action to create the object.
	 * 
	 * @return
	 */
	public PlanimodManagers getManagers() {
		return this.managers;
	}

	/**
	 * Return the shell provider given in the constructor.
	 * 
	 * @return
	 */
	public IShellProvider getShellProvider() {
		return this.shellProvider;
	}

	/**
	 * Return the selection provider or null if not set.
	 * 
	 * @return
	 */
	public ISelectionProvider getSelectionProvider() {
		return this.selectionProvider;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.jface.action.Action#run()
	 */
	@Override
	public void run() {

		if (!canCreateObject()) {
			MessageUtils.openWarning(this.shellProvider.getShell(), Localized
					.get(AbstractAddAction.class,
							"AbstractAddAction.cantCreateObject"), //$NON-NLS-1$
					null, (String) null);
			return;
		}

		// An error occurred
		final List<? extends ManagedObject> list;
		try {
			list = createObjects();
		} catch (IntegrityException e) {
			MessageUtils.openWarning(this.shellProvider.getShell(), Localized
					.get(AbstractAddAction.class,
							"AbstractAddAction.cantCreateObject"), //$NON-NLS-1$
					e.getLocalizedMessage(), (String) null);
			return;
		} catch (ManagerException e1) {
			MessageUtils.openWarning(this.shellProvider.getShell(), Localized
					.get(AbstractAddAction.class,
							"AbstractAddAction.cantCreateObject"), //$NON-NLS-1$
					Localized.get(AbstractAddAction.class,
							"AbstractAddAction.errorOccurred"), e1);//$NON-NLS-1$
			return;
		}
		if (list == null || list.isEmpty()) {
			MessageUtils.openWarning(this.shellProvider.getShell(), Localized
					.get(AbstractAddAction.class,
							"AbstractAddAction.cantCreateObject"),//$NON-NLS-1$
					null, (String) null);
			return;
		}

		// Add objects
		try {
			this.managers.addAll(list);
		} catch (ManagerException e2) {
			MessageUtils.openWarning(this.shellProvider.getShell(), Localized
					.get(AbstractAddAction.class,
							"AbstractAddAction.cantCreateObject"),//$NON-NLS-1$
					Localized.get(AbstractAddAction.class,
							"AbstractAddAction.errorOccurred"), e2);//$NON-NLS-1$
		}

		// Select object in the viewer
		if (getSelectionProvider() instanceof StructuredViewer) {
			Display.getDefault().asyncExec(new Runnable() {
				@Override
				public void run() {
					selectObjects(list);
				}
			});
		}

	}

	/**
	 * Called to select into the selection provider if define.
	 * 
	 * @param list
	 */
	protected void selectObjects(Collection<? extends ManagedObject> list) {
		((StructuredViewer) getSelectionProvider()).setSelection(
				new StructuredSelection(list.toArray()), true);
	}

}
