/*******************************************************************************
 * Copyright (c) 2004, 2015 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.core.commands;

/**
 * <p>
 * An event indicating that the set of defined command identifiers has changed.
 * </p>
 *
 * @since 3.1
 * @see ICommandManagerListener#commandManagerChanged(CommandManagerEvent)
 */
public final class CommandManagerEvent {

	/**
	 * The bit used to represent whether the given category has become defined.
	 * If this bit is not set and there is no category id, then no category has
	 * become defined nor undefined. If this bit is not set and there is a
	 * category id, then the category has become undefined.
	 */
	private static final int CHANGED_CATEGORY_DEFINED = 1;

	/**
	 * The bit used to represent whether the given command has become defined.
	 * If this bit is not set and there is no command id, then no command has
	 * become defined nor undefined. If this bit is not set and there is a
	 * command id, then the command has become undefined.
	 */
	private static final int CHANGED_COMMAND_DEFINED = 1 << 1;

	/**
	 * The bit used to represent whether the given command parameter type has
	 * become defined. If this bit is not set and there is no parameter type id,
	 * then no parameter type has become defined nor undefined. If this bit is
	 * not set and there is a parameter type id, then the parameter type has
	 * become undefined.
	 *
	 * @since 3.2
	 */
	private static final int CHANGED_PARAMETER_TYPE_DEFINED = 1 << 2;

	/**
	 * The category identifier that was added or removed from the list of
	 * defined category identifiers. This value is <code>null</code> if the
	 * list of defined category identifiers did not change.
	 */
	private final String categoryId;

	/**
	 * A collection of bits representing whether certain values have changed. A
	 * bit is set (i.e., <code>1</code>) if the corresponding property has
	 * changed.
	 */
	private final int changedValues;

	/**
	 * The command identifier that was added or removed from the list of defined
	 * command identifiers. This value is <code>null</code> if the list of
	 * defined command identifiers did not change.
	 */
	private final String commandId;

	/**
	 * The command parameter type identifier that was added or removed from the
	 * list of defined parameter type identifiers. This value is
	 * <code>null</code> if the list of defined parameter type identifiers did
	 * not change.
	 *
	 * @since 3.2
	 */
	private final String parameterTypeId;

	/**
	 * The command manager that has changed.
	 */
	private final CommandManager commandManager;

	/**
	 * Creates a new <code>CommandManagerEvent</code> instance to describe
	 * changes to commands and/or categories.
	 *
	 * @param commandManager
	 *            the instance of the interface that changed; must not be
	 *            <code>null</code>.
	 * @param commandId
	 *            The command identifier that was added or removed; must not be
	 *            <code>null</code> if commandIdChanged is <code>true</code>.
	 * @param commandIdAdded
	 *            Whether the command identifier became defined (otherwise, it
	 *            became undefined).
	 * @param commandIdChanged
	 *            Whether the list of defined command identifiers has changed.
	 * @param categoryId
	 *            The category identifier that was added or removed; must not be
	 *            <code>null</code> if categoryIdChanged is <code>true</code>.
	 * @param categoryIdAdded
	 *            Whether the category identifier became defined (otherwise, it
	 *            became undefined).
	 * @param categoryIdChanged
	 *            Whether the list of defined category identifiers has changed.
	 */
	public CommandManagerEvent(final CommandManager commandManager,
			final String commandId, final boolean commandIdAdded,
			final boolean commandIdChanged, final String categoryId,
			final boolean categoryIdAdded, final boolean categoryIdChanged) {
		if (commandManager == null) {
			throw new NullPointerException(
					"An event must refer to its command manager"); //$NON-NLS-1$
		}

		if (commandIdChanged && (commandId == null)) {
			throw new NullPointerException(
					"If the list of defined commands changed, then the added/removed command must be mentioned"); //$NON-NLS-1$
		}

		if (categoryIdChanged && (categoryId == null)) {
			throw new NullPointerException(
					"If the list of defined categories changed, then the added/removed category must be mentioned"); //$NON-NLS-1$
		}

		this.commandManager = commandManager;
		this.commandId = commandId;
		this.categoryId = categoryId;

		// this constructor only works for changes to commands and categories
		this.parameterTypeId = null;

		int changedValues = 0;
		if (categoryIdChanged && categoryIdAdded) {
			changedValues |= CHANGED_CATEGORY_DEFINED;
		}
		if (commandIdChanged && commandIdAdded) {
			changedValues |= CHANGED_COMMAND_DEFINED;
		}
		this.changedValues = changedValues;
	}

	/**
	 * Creates a new <code>CommandManagerEvent</code> instance to describe
	 * changes to command parameter types.
	 *
	 * @param commandManager
	 *            the instance of the interface that changed; must not be
	 *            <code>null</code>.
	 * @param parameterTypeId
	 *            The command parameter type identifier that was added or
	 *            removed; must not be <code>null</code> if
	 *            parameterTypeIdChanged is <code>true</code>.
	 * @param parameterTypeIdAdded
	 *            Whether the parameter type identifier became defined
	 *            (otherwise, it became undefined).
	 * @param parameterTypeIdChanged
	 *            Whether the list of defined parameter type identifiers has
	 *            changed.
	 *
	 * @since 3.2
	 */
	public CommandManagerEvent(final CommandManager commandManager,
			final String parameterTypeId, final boolean parameterTypeIdAdded,
			final boolean parameterTypeIdChanged) {

		if (commandManager == null) {
			throw new NullPointerException(
					"An event must refer to its command manager"); //$NON-NLS-1$
		}

		if (parameterTypeIdChanged && (parameterTypeId == null)) {
			throw new NullPointerException(
					"If the list of defined command parameter types changed, then the added/removed parameter type must be mentioned"); //$NON-NLS-1$
		}

		this.commandManager = commandManager;
		this.commandId = null;
		this.categoryId = null;

		this.parameterTypeId = parameterTypeId;

		int changedValues = 0;
		if (parameterTypeIdChanged && parameterTypeIdAdded) {
			changedValues |= CHANGED_PARAMETER_TYPE_DEFINED;
		}

		this.changedValues = changedValues;
	}

	/**
	 * Returns the category identifier that was added or removed.
	 *
	 * @return The category identifier that was added or removed; may be
	 *         <code>null</code>.
	 */
	public final String getCategoryId() {
		return categoryId;
	}

	/**
	 * Returns the command identifier that was added or removed.
	 *
	 * @return The command identifier that was added or removed; may be
	 *         <code>null</code>.
	 */
	public final String getCommandId() {
		return commandId;
	}

	/**
	 * Returns the instance of the interface that changed.
	 *
	 * @return the instance of the interface that changed. Guaranteed not to be
	 *         <code>null</code>.
	 */
	public final CommandManager getCommandManager() {
		return commandManager;
	}

	/**
	 * Returns the command parameter type identifier that was added or removed.
	 *
	 * @return The command parameter type identifier that was added or removed;
	 *         may be <code>null</code>.
	 *
	 * @since 3.2
	 */
	public final String getParameterTypeId() {
		return parameterTypeId;
	}

	/**
	 * Returns whether the list of defined category identifiers has changed.
	 *
	 * @return <code>true</code> if the list of category identifiers has
	 *         changed; <code>false</code> otherwise.
	 */
	public final boolean isCategoryChanged() {
		return (categoryId != null);
	}

	/**
	 * Returns whether the category identifier became defined. Otherwise, the
	 * category identifier became undefined.
	 *
	 * @return <code>true</code> if the category identifier became defined;
	 *         <code>false</code> if the category identifier became undefined.
	 */
	public final boolean isCategoryDefined() {
		return (((changedValues & CHANGED_CATEGORY_DEFINED) != 0) && (categoryId != null));
	}

	/**
	 * Returns whether the list of defined command identifiers has changed.
	 *
	 * @return <code>true</code> if the list of command identifiers has
	 *         changed; <code>false</code> otherwise.
	 */
	public final boolean isCommandChanged() {
		return (commandId != null);
	}

	/**
	 * Returns whether the command identifier became defined. Otherwise, the
	 * command identifier became undefined.
	 *
	 * @return <code>true</code> if the command identifier became defined;
	 *         <code>false</code> if the command identifier became undefined.
	 */
	public final boolean isCommandDefined() {
		return (((changedValues & CHANGED_COMMAND_DEFINED) != 0) && (commandId != null));
	}

	/**
	 * Returns whether the list of defined command parameter type identifiers
	 * has changed.
	 *
	 * @return <code>true</code> if the list of command parameter type
	 *         identifiers has changed; <code>false</code> otherwise.
	 *
	 * @since 3.2
	 */
	public final boolean isParameterTypeChanged() {
		return (parameterTypeId != null);
	}

	/**
	 * Returns whether the command parameter type identifier became defined.
	 * Otherwise, the command parameter type identifier became undefined.
	 *
	 * @return <code>true</code> if the command parameter type identifier
	 *         became defined; <code>false</code> if the command parameter
	 *         type identifier became undefined.
	 *
	 * @since 3.2
	 */
	public final boolean isParameterTypeDefined() {
		return (((changedValues & CHANGED_PARAMETER_TYPE_DEFINED) != 0) && (parameterTypeId != null));
	}
}
