//------------------------------------------------------------------------------
// Copyright (c) 2005, 2006 IBM Corporation and others.
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// which accompanies this distribution, and is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// Contributors:
// IBM Corporation - initial implementation
//------------------------------------------------------------------------------
package org.eclipse.epf.library.edit.validation;

import java.util.Collection;
import java.util.Iterator;

import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.epf.common.utils.StrUtil;
import org.eclipse.epf.library.edit.IFilter;
import org.eclipse.epf.library.edit.LibraryEditResources;
import org.eclipse.epf.library.edit.process.BSActivityItemProvider;
import org.eclipse.epf.library.edit.util.ProcessUtil;
import org.eclipse.epf.library.edit.util.Suppression;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.NamedElement;
import org.eclipse.epf.uma.RoleDescriptor;
import org.eclipse.epf.uma.TaskDescriptor;
import org.eclipse.epf.uma.TeamProfile;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.WorkProductDescriptor;
import org.eclipse.osgi.util.NLS;


/**
 * @author Phong Nguyen Le - Jan 31, 2006
 * @since 1.0
 */
public final class NameChecker {

	private static final String ELEMENT_TEXT = LibraryEditResources.element_text; //$NON-NLS-1$

	private static String checkName(AdapterFactory adapterFactory,
			Object parent, Object e, final Class type,
			INameProvider nameProvider, String newName, Suppression suppresion, boolean ignoreSuppressed) {
		IFilter childFilter = new IFilter() {

			public boolean accept(Object obj) {
				return type.isInstance(obj);
			}

		};

		return checkName(adapterFactory, parent, e, childFilter, nameProvider,
				newName, suppresion, ignoreSuppressed );
	}

	private static String checkName(AdapterFactory adapterFactory,
			Object parent, Object e, IFilter childFilter,
			EStructuralFeature nameFeature, String newName,
			Suppression suppression, boolean ignoreSuppressed) {
		return checkName(adapterFactory, parent, e, childFilter,
				createNameProvider(nameFeature), newName, suppression, ignoreSuppressed);
	}

	private static String checkName(AdapterFactory adapterFactory,
			Object parent, Object e, IFilter childFilter,
			INameProvider nameProvider, String newName, Suppression suppression, 
			boolean ignoreSuppressed) {
		ITreeItemContentProvider adapter = (ITreeItemContentProvider) adapterFactory
				.adapt(parent, ITreeItemContentProvider.class);
		Collection children;
		
		if (ignoreSuppressed)
			if (suppression != null  && suppression.isSuppressed(e))
			{
				return null;
			}
		
		// get children of the rolled-down dapter
		//
		boolean wasRolledUp = false;
		BSActivityItemProvider bsItemProvider = null;
		if(adapter instanceof BSActivityItemProvider) {
			bsItemProvider = ((BSActivityItemProvider)adapter); 
			wasRolledUp = bsItemProvider.isRolledUp();
		}
		try {
			if(wasRolledUp) {
				bsItemProvider.setRolledUp(false);
			}
			children = adapter.getChildren(parent);
		} catch (NullPointerException ex) {
			throw ex;
		}
		finally {
			if(wasRolledUp) {
				bsItemProvider.basicSetRolledUp(wasRolledUp);
			}
		}
				
		for (Iterator iter = children.iterator(); iter.hasNext();) {
			Object item = iter.next();

			// skip checking on suppressed element
			//
			if (suppression != null && suppression.isSuppressed(item)) {
				continue;
			}

			Object child = TngUtil.unwrap(item);
			if (child != e && childFilter.accept(child)) {
				String name = nameProvider.getName(child);
				if (name.equalsIgnoreCase(newName)) {
					//		return I18nUtil.formatString(RESOURCE_BUNDLE, key, data);
					return NLS.bind(LibraryEditResources.duplicateElementNameError_msg, name); 
				}
			}
		}
		return null;
	}

	private static INameProvider createNameProvider(
			final EStructuralFeature nameFeature) {
		return new INameProvider() {

			public String getName(Object object) {
				if (object instanceof BreakdownElement
						&& nameFeature == UmaPackage.eINSTANCE
								.getDescribableElement_PresentationName()) {
					return ProcessUtil
							.getPresentationName((BreakdownElement) object);
				} else if (object instanceof EObject) {
					return (String) ((EObject) object).eGet(nameFeature);
				} else {
					return null;
				}
			}

		};
	}

	public static String checkName(AdapterFactory adapterFactory,
			final Object e, Class type, final EStructuralFeature nameFeature,
			String newName, Suppression suppression) {
		return checkName(adapterFactory, e, type,
				createNameProvider(nameFeature), newName, suppression, false);
	}


	private static String checkName(AdapterFactory adapterFactory, Object e,
			Class type, INameProvider nameProvider, String newName,
			Suppression suppression, boolean ignoreSuppressed) {
		String elementType;
		if (e instanceof NamedElement) {
			elementType = TngUtil.getTypeText((NamedElement) e);
		} else {
			elementType = ELEMENT_TEXT;
		}
		if (newName == null || newName.trim().length() == 0) {
			//		return I18nUtil.formatString(RESOURCE_BUNDLE, key, data);
			return NLS.bind(LibraryEditResources.emptyElementNameError_msg, StrUtil.toLower(elementType)); 
		}
		ITreeItemContentProvider adapter = (ITreeItemContentProvider) adapterFactory
				.adapt(e, ITreeItemContentProvider.class);
		Object parent = adapter.getParent(e);
		if (parent == null)
			return null;
		return checkName(adapterFactory, parent, e, type, nameProvider,
				newName, suppression, ignoreSuppressed);
	}

	/**
	 * Used by
	 * {@link #checkName(AdapterFactory, BreakdownElement, EStructuralFeature, String) checkName()}
	 * 
	 * @param e
	 * @return
	 */
	private static Class chooseType(BreakdownElement e) {
		if (e instanceof Activity) {
			return Activity.class;
		} else if (e instanceof TaskDescriptor) {
			return TaskDescriptor.class;
		} else if (e instanceof RoleDescriptor) {
			return RoleDescriptor.class;
		} else if (e instanceof WorkProductDescriptor) {
			return WorkProductDescriptor.class;
		} else {
			return e.getClass();
		}
		// return BreakdownElement.class;
	}

	private static String checkNameInScope(AdapterFactory adapterFactory,
			BreakdownElement e, EStructuralFeature feature, String name,
			IFilter childFilter, Suppression suppression, boolean ignoreSuppressed) {
		// check up
		ITreeItemContentProvider itemProvider = (ITreeItemContentProvider) adapterFactory
				.adapt(e, ITreeItemContentProvider.class);
		Object currentParent = itemProvider.getParent(e);
		for (Object parent = currentParent; parent != null;) {
			String msg = checkName(adapterFactory, parent, e, childFilter,
					feature, name, suppression, ignoreSuppressed);
			if (msg != null)
				return msg;
			itemProvider = (ITreeItemContentProvider) adapterFactory.adapt(
					parent, ITreeItemContentProvider.class);
			parent = itemProvider.getParent(parent);
		}

		// check down
		if (currentParent != null) {
			Iterator iter = new AdapterFactoryTreeIterator(adapterFactory,
					currentParent, false);
			while (iter.hasNext()) {
				Object child = TngUtil.unwrap(iter.next());
				if (child != e && childFilter.accept(child)) {
					String childName = (String) ((EObject) child).eGet(feature);
					if (name.equalsIgnoreCase(childName)) {
						//		return I18nUtil.formatString(RESOURCE_BUNDLE, key, data);
						return NLS.bind(LibraryEditResources.duplicateElementNameError_msg, name); 
					}

				}
			}
		}

		return null;
	}

	public static String checkName(AdapterFactory adapterFactory,
			final BreakdownElement e, EStructuralFeature feature, String name,
			Suppression suppression) {
		
		return checkName_(adapterFactory,
				e, feature, name,
				suppression, false);
		
	}
	
	public static String checkName(AdapterFactory adapterFactory,
			final BreakdownElement e, EStructuralFeature feature, String name,
			Suppression suppression, boolean ignoreSuppressed) {
		
		return checkName_(adapterFactory,
				e, feature, name,
				suppression, ignoreSuppressed);
		
	}
	
	private static String checkName_(AdapterFactory adapterFactory,
			final BreakdownElement e, EStructuralFeature feature, String name,
			Suppression suppression, boolean ignoreSuppressed) {
		Class type = chooseType(e);

		// basic check
		//
		String msg = null;
		if (ignoreSuppressed)
			msg = checkName(adapterFactory, e, type,
					createNameProvider(feature), name, suppression, ignoreSuppressed); 
		else
			msg = checkName(adapterFactory, e, type, feature, name,
					suppression);
			
		if (msg != null)
			return msg;
		IFilter childFilter = null;
		if (e instanceof TeamProfile) {
			// don't allow team with the same name within the same scope
			//
			childFilter = new IFilter() {
				public boolean accept(Object obj) {
					return e instanceof TeamProfile;
				}
			};
		}
		// PLEASE DON'T CLEAN UP.
		// Commented out for now, since we change scope of Deliverable.
		// else if (e instanceof WorkProductDescriptor
		// && ((WorkProductDescriptor) e).getWorkProduct() instanceof
		// Deliverable) {
		// childFilter = new IFilter() {
		//	
		// public boolean accept(Object obj) {
		// return e instanceof WorkProductDescriptor
		// && ((WorkProductDescriptor) e).getWorkProduct() instanceof
		// Deliverable;
		// }
		// };
		// }

		if (childFilter != null) {
			return checkNameInScope(adapterFactory, e, feature, name,
					childFilter, suppression, ignoreSuppressed);
		}

		return null;
	}
}
