/*
 * 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.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.IShellProvider;

import com.patrikdufresne.jface.dialogs.DetailMessageDialog;
import com.patrikdufresne.managers.ArchivableObject;
import com.patrikdufresne.managers.ManagedObject;
import com.patrikdufresne.managers.ManagerException;
import com.patrikdufresne.util.Localized;
import com.planimod.core.PlanimodManagers;
import com.planimod.ui.theme.Resources;

/**
 * Action to remove and/or archive Managed object.
 * 
 * @author Patrik Dufresne
 * 
 */
public class RemoveArchiveAction extends Action {
	/**
	 * Property name of this action objects (value <code>"objects"</code>). The
	 * objects property define the entities to be deleted.
	 */
	public static final String OBJECTS = "objects"; //$NON-NLS-1$

	/**
	 * Used for localized.
	 */
	private Localized localized = Localized.load(RemoveArchiveAction.class);

	/**
	 * Managers used to remove the objects.
	 */
	private PlanimodManagers managers;

	/**
	 * The list of object to be removed.
	 */
	private List<ManagedObject> objects;

	/**
	 * Shell provider.
	 */
	private IShellProvider shellProvider;

	/**
	 * Create the action
	 * 
	 * @param provider
	 *            the selection provider
	 * @param shellProvider
	 *            the shell provider
	 */
	public RemoveArchiveAction(PlanimodManagers managers,
			IShellProvider shellProvider) {
		if (managers == null || shellProvider == null) {
			throw new NullPointerException();
		}
		this.managers = managers;
		this.shellProvider = shellProvider;
		setText(Localized.get(RemoveArchiveAction.class,
				"RemoveArchiveAction.text")); //$NON-NLS-1$
		setToolTipText(Localized.get(RemoveArchiveAction.class,
				"RemoveArchiveAction.toolTipText")); //$NON-NLS-1$
		setImageDescriptor(Resources
				.getImageDescriptor(Resources.ICON_LIST_REMOVE_16));
		setEnabled(canRun());
	}

	/**
	 * Check if this action can be run.
	 * <p>
	 * This implementation check if the objects list is set and if it contains
	 * archivable objects.
	 * 
	 * @return True if the action can be run.
	 */
	protected boolean canRun() {
		if (this.objects == null || this.objects.size() == 0) {
			return false;
		}
		Iterator<ManagedObject> iter = this.objects.iterator();
		while (iter.hasNext()) {
			ManagedObject obj = iter.next();
			if (!(obj instanceof ArchivableObject)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Return an unmodifiable collection of object to be removed by this action.
	 * 
	 * @return
	 */
	public List<ManagedObject> getObjects() {
		return this.objects;
	}

	/**
	 * The remove button id.
	 * 
	 * @See MessageDialogWithToggle
	 */
	private static final int REMOVE_ID = IDialogConstants.INTERNAL_ID;
	/**
	 * The archive button id.
	 * 
	 * @See MessageDialogWithToggle
	 */
	private static final int ARCHIVE_ID = IDialogConstants.INTERNAL_ID + 1;

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

		if (!canRun()) {
			return;
		}

		String[] buttons = new String[] {
				this.localized.get("RemoveArchiveAction.removeLabel"), //$NON-NLS-1$
				this.localized.get("RemoveArchiveAction.archiveLabel"), //$NON-NLS-1$
				IDialogConstants.CANCEL_LABEL };

		// Prompt the user to confirm the remove or the archive
		DetailMessageDialog dlg = new DetailMessageDialog(
				this.shellProvider.getShell(),
				this.localized.get("RemoveArchiveAction.confirmMessage.title"), //$NON-NLS-1$
				null,
				this.localized
						.get("RemoveArchiveAction.confirmMessage.message"), //$NON-NLS-1$
				this.localized
						.get("RemoveArchiveAction.confirmMessage.shortDetail"), //$NON-NLS-1$
				null, MessageDialog.QUESTION, buttons, 2, null, false);

		int operation = dlg.open();
		if (operation != REMOVE_ID && operation != ARCHIVE_ID) {
			return;
		}

		try {
			switch (operation) {
			case REMOVE_ID:
				this.managers.removeAll(this.objects);
				break;
			case ARCHIVE_ID:
				this.managers.archiveAll(this.objects);
				break;
			}
		} catch (ManagerException e) {

			handleException(operation, e);

		}
		// TODO release the object array
	}

	/**
	 * This function is used to display an error message to the user.
	 * 
	 * @param e
	 *            the exception.
	 */
	private void handleException(int operation, ManagerException e) {

		String message = operation == REMOVE_ID ? this.localized
				.get("RemoveArchiveAction.cantRemoveObject") //$NON-NLS-1$
				: this.localized.get("RemoveArchiveAction.cantArchiveObject"); //$NON-NLS-1$
		String shortDetail = this.localized
				.get("RemoveArchiveAction.unknownException"); //$NON-NLS-1$

		// Check the cause to provide a better error message
		if (e.isConstraintViolationException()) {
			shortDetail = this.localized
					.get("RemoveArchiveAction.constraintViolationException"); //$NON-NLS-1$
		}

		StringWriter sw = new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		DetailMessageDialog.openDetailWarning(this.shellProvider.getShell(),
				this.localized.get("RemoveArchiveAction.exception.title"), //$NON-NLS-1$
				message, shortDetail, sw.toString());
	}

	/**
	 * Sets the objects to be remove or archived.
	 * 
	 * @param objects
	 *            the object collection or null to unset.
	 */
	public void setObjects(List<ManagedObject> objects) {
		firePropertyChange(
				OBJECTS,
				getObjects(),
				this.objects = (objects == null || objects.size() == 0 ? null
						: Collections
								.unmodifiableList(new ArrayList<ManagedObject>(
										objects))));
		setEnabled(canRun());
	}

}
