/*******************************************************************************
 * Copyright (c) 2001, 2005 IBM Corporation and others.
 * All rights reserved. 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
 *     Jens Lukowski/Innoopract - initial renaming/restructuring
 *     
 *******************************************************************************/
package org.eclipse.wst.sse.core.internal.model;

import org.eclipse.wst.sse.core.internal.Logger;
import org.eclipse.wst.sse.core.internal.provisional.IModelLifecycleListener;
import org.eclipse.wst.sse.core.internal.util.Utilities;


/**
 * For "internal use" only by AbstractStructuredModel
 */

class LifecycleNotificationManager {
	private Object[] fListeners;

	LifecycleNotificationManager() {
		super();
	}

	/**
	 * Adds a new copy of the given listener to the list of Life Cycle
	 * Listeners.
	 * 
	 * Multiple copies of the same listener are allowed. This is required to
	 * support threaded listener management properly and for model-driven move
	 * to work. For example, two adds and a single remove should result in the
	 * listener still listening for events.
	 * 
	 * @param listener
	 */
	void addListener(IModelLifecycleListener listener) {
		if (Logger.DEBUG && Utilities.contains(fListeners, listener)) {
			Logger.log(Logger.WARNING, "IModelLifecycleListener " + listener + " listening more than once"); //$NON-NLS-1$ //$NON-NLS-2$
		}
		int oldSize = 0;
		if (fListeners != null) {
			// normally won't be null, but we need to be sure, for first
			// time through
			oldSize = fListeners.length;
		}
		int newSize = oldSize + 1;
		Object[] newListeners = new Object[newSize];
		if (fListeners != null) {
			System.arraycopy(fListeners, 0, newListeners, 0, oldSize);
		}
		// add listener to last position
		newListeners[newSize - 1] = listener;
		//
		// now switch new for old
		fListeners = newListeners;
	}

	/**
	 * Removes a single copy of the given listener from the list of Life Cycle
	 * Listeners.
	 * 
	 * @param listener
	 */
	void removeListener(IModelLifecycleListener listener) {
		if (Utilities.contains(fListeners, listener)) {
			// if its not in the listeners, we'll ignore the request
			int oldSize = fListeners.length;
			int newSize = oldSize - 1;
			Object[] newListeners = new Object[newSize];
			int index = 0;
			boolean removedOnce = false;
			for (int i = 0; i < oldSize; i++) {
				if (fListeners[i] == listener && !removedOnce) {
					// ignore on the first match
					removedOnce = true;
				} else {
					// copy old to new if it's not the one we are removing
					newListeners[index++] = fListeners[i];
				}
			}
			// now that we have a new array, let's switch it for the old
			// one
			fListeners = newListeners;
		}
		if (Logger.DEBUG && Utilities.contains(fListeners, listener)) {
			Logger.log(Logger.WARNING, "IModelLifecycleListener " + listener + " removed once but still listening"); //$NON-NLS-1$ //$NON-NLS-2$
		}
	}

	void signalLifecycleEvent(ModelLifecycleEvent event) {
		if (Logger.DEBUG_LIFECYCLE) {
			Logger.log(Logger.INFO, "ModelLifecycleEvent fired for " + event.getModel().getId() + ": " + event.toString()); //$NON-NLS-1$ //$NON-NLS-2$
			System.out.println("ModelLifecycleEvent fired for " + event.getModel().getId() + ": " + event.toString()); //$NON-NLS-1$ //$NON-NLS-2$
		}
		// We must assign listeners to local variable, since the add and
		// remove listener methods can change the actual instance of the
		// listener array from another thread
		if (fListeners != null) {
			Object[] holdListeners = fListeners;
			for (int i = 0; i < holdListeners.length; i++) {
				IModelLifecycleListener listener = (IModelLifecycleListener) holdListeners[i];
				// only one type of listener for now ... this could become
				// more complex
				if ((event.getInternalType() & ModelLifecycleEvent.PRE_EVENT) == ModelLifecycleEvent.PRE_EVENT) {
					listener.processPreModelEvent(event);
				}
				if ((event.getInternalType() & ModelLifecycleEvent.POST_EVENT) == ModelLifecycleEvent.POST_EVENT) {
					listener.processPostModelEvent(event);
				}
			}
		}
	}
}
