//------------------------------------------------------------------------------
// 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.process;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.command.RemoveCommand;
import org.eclipse.emf.edit.command.CopyCommand.Helper;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.IEditingDomainItemProvider;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.provider.ItemProviderAdapter;
import org.eclipse.emf.edit.provider.ViewerNotification;
import org.eclipse.epf.library.edit.ICachedChildrenItemProvider;
import org.eclipse.epf.library.edit.command.IActionManager;
import org.eclipse.epf.library.edit.process.command.WorkProductDescriptorCreateCopyCommand;
import org.eclipse.epf.library.edit.util.ProcessUtil;
import org.eclipse.epf.library.edit.util.TngUtil;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.Artifact;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.Deliverable;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.WorkProduct;
import org.eclipse.epf.uma.WorkProductDescriptor;
import org.eclipse.epf.uma.util.UmaUtil;


/**
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class WorkProductDescriptorItemProvider extends DescriptorItemProvider
implements ICachedChildrenItemProvider
{
	protected Collection cachedChildren;
	
	/**
	 * @param adapterFactory
	 */
	public WorkProductDescriptorItemProvider(AdapterFactory adapterFactory,
			ItemProviderAdapter delegateItemProvider) {
		super(adapterFactory, delegateItemProvider);
	}

	public Activity getActivity(Object object) {
		for (Object parent = getParent(object); parent != null;) {
			if (parent instanceof Activity)
				return (Activity) parent;
			AdapterFactory aFactory = getRootAdapterFactory();
			ItemProviderAdapter adapter = (ItemProviderAdapter) aFactory.adapt(
					parent, ITreeItemContentProvider.class);
			parent = adapter.getParent(parent);
		}
		return null;
	}

	private Object getDescriptor(Activity activity, Artifact artifact) {
		if (activity == null)
			return null;
		List list = activity.getBreakdownElements();
		int size = list.size();
		for (int i = 0; i < size; i++) {
			Object obj = list.get(i);
			if ((obj instanceof WorkProductDescriptor)
					&& (artifact == ((WorkProductDescriptor) obj)
							.getWorkProduct())) {
				return obj;
			}
		}
		return null;
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildrenFeatures(java.lang.Object)
	 */
	public Collection getChildrenFeatures(Object object) {
		// if (childrenFeatures == null)
		// {
		// childrenFeatures = new ArrayList();
		// childrenFeatures.add(UmaPackage.eINSTANCE.getWorkProductDescriptor_DeliverableParts());
		// }
		// return childrenFeatures;

		return Collections.EMPTY_LIST;
	}
	
	private void updateCachedChildren(Collection children) {
//		if(cachedChildren == null) {
//			cachedChildren = new ArrayList(children);
//		}
//		else {
//			cachedChildren.clear();
//			cachedChildren.addAll(children);
//		}
	}
	
	protected void addContainedArtifactDescriptors(WorkProductDescriptor wpDesc, Collection children) {
		Activity activity = UmaUtil.getParentActivity(wpDesc);
		if(activity != null) {
			Artifact artifact = (Artifact) wpDesc.getWorkProduct();
			List list = artifact.getContainedArtifacts();
			for (int i = 0; i < list.size(); i++) {
				Object descriptor = getDescriptor(activity, (Artifact) list
						.get(i));
				if (descriptor != null) {
					children.add(descriptor);
				}
			}
			
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#getChildren(java.lang.Object)
	 */
	public Collection getChildren(Object object) {
		WorkProductDescriptor wpDesc = (WorkProductDescriptor) object;
		
		// don't show the children of the non-deliverable work product descriptor
		// that is deliverable part of a deliverable work product descriptor
		//
		if(!(wpDesc.getWorkProduct() instanceof Deliverable)) {
			Object parent = TngUtil.unwrap(getParent(object));
			if(parent instanceof WorkProductDescriptor
					&& ((WorkProductDescriptor)parent).getWorkProduct() instanceof Deliverable) {				
				updateCachedChildren(Collections.EMPTY_LIST);
				return Collections.EMPTY_LIST;
			}
		}
		
		Collection children = super.getChildren(object);

		// add deliverable parts
		//
		if(!wpDesc.getDeliverableParts().isEmpty()) {
			for (Iterator iter = wpDesc.getDeliverableParts().iterator(); iter
			.hasNext();) {
				WorkProductDescriptor desc = (WorkProductDescriptor) iter.next();
				if (desc.getSuperActivities() == null) {
					children.add(desc);
				}
			}
		}
		
		// get descriptors of contained artifacts
		//		
		if (wpDesc.getWorkProduct() instanceof Artifact) {
			addContainedArtifactDescriptors(wpDesc, children);
		}

		// set parent
		//
		AdapterFactory aFactory = getRootAdapterFactory();

		for (Iterator iter = children.iterator(); iter.hasNext();) {
			IBSItemProvider adapter = (IBSItemProvider) aFactory.adapt(iter
					.next(), ITreeItemContentProvider.class);
			adapter.setParent(object);
		}
		
		updateCachedChildren(children);
		
		return children;
	}
	
	/**
	 * Gets children from cache, fill the children cache if it is not initialized yet
	 * 
	 * @return
	 */
	public Collection getChildrenFromCache() {
		if(cachedChildren == null) {
			getChildren(target);
		}
		return cachedChildren;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.epf.library.edit.ICachedChildrenItemProvider#getRollupChildrenFromCache()
	 */
	public Collection getRollupChildrenFromCache() {
		return Collections.EMPTY_LIST;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.epf.library.edit.process.BreakdownElementItemProvider#dispose()
	 */
	public void dispose() {
		if(cachedChildren != null) {
			cachedChildren.clear();
			cachedChildren = null;
		}
		
		super.dispose();
	}

	public void notifyChanged(Notification notification) {
		switch (notification.getFeatureID(WorkProductDescriptor.class)) {
//		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__MANDATORY_INPUT_TO:
//		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__OPTIONAL_INPUT_TO:
//		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__EXTERNAL_INPUT_TO:
//		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__OUTPUT_FROM:
		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__ACTIVITY_ENTRY_STATE:
		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__ACTIVITY_EXIT_STATE:
			fireNotifyChanged(new ViewerNotification(notification, notification
					.getNotifier(), false, true));
			return;
		case UmaPackage.WORK_PRODUCT_DESCRIPTOR__DELIVERABLE_PARTS:
			List elements = ProcessUtil.getAffectedElements(notification, null);
			int eventType = notification.getEventType();
			boolean refresh = false;
			for (Iterator iter = elements.iterator(); iter.hasNext();) {
				BreakdownElement e = (BreakdownElement) iter.next();
				if (eventType == Notification.ADD
						|| eventType == Notification.ADD_MANY) {
					ProcessUtil.addToContainer(e, this, false);
				}
				if (e.getSuperActivities() != null) {
					fireNotifyChanged(new ViewerNotification(notification, e,
							false, true));
				} else {
					refresh = true;
				}
			}

			if (refresh) {
				fireNotifyChanged(new ViewerNotification(notification,
						notification.getNotifier(), true, false));
			}

			return;
		case UmaPackage.DESCRIBABLE_ELEMENT__PRESENTATION_NAME:
			WorkProductDescriptor wpd = (WorkProductDescriptor) notification
					.getNotifier();
			fireNotifyChanged(new ViewerNotification(notification, wpd, false,
					true));
			if (wpd.getWorkProduct() instanceof Deliverable) {
				for (Iterator iter = wpd.getDeliverableParts().iterator(); iter
						.hasNext();) {
					Object descriptor = iter.next();
					fireNotifyChanged(new ViewerNotification(notification,
							descriptor, false, true));
				}
			}
			return;
		}

		super.notifyChanged(notification);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.IBSItemProvider#setAttribute(java.lang.Object,
	 *      java.lang.String, java.lang.String)
	 */
	public void setAttribute(Object object, String prop, String txt) {
		WorkProductDescriptor wpd = (WorkProductDescriptor) object;

		if (prop == IBSItemProvider.COL_ENTRY_STATE) {
			wpd.setActivityEntryState(txt);
		} else if (prop == IBSItemProvider.COL_EXIT_STATE) {
			wpd.setActivityExitState(txt);
		} else {
			super.setAttribute(object, prop, txt);
		}
	}

	// private boolean isTopLevelArtifactInActivity(Activity activity, Object
	// obj)
	// {
	// AdapterFactory aFactory = TngUtil.getBestAdapterFactory(adapterFactory);
	//        
	// BSActivityItemProvider adapter = (BSActivityItemProvider)
	// aFactory.adapt(activity, ITreeItemContentProvider.class);
	// Collection children = adapter.getChildren(activity);
	// if (children.contains(obj))
	// {
	// //System.out.println("child - " + obj);
	// return true;
	// }
	// return false;
	// }

	public Collection getEClasses() {
		return ProcessUtil.getPBSEclasses();
	}

	public void moveUp(Object obj, IActionManager actionMgr) {
		Object parent = this.getParent(obj);
		if ((parent != null) && (parent instanceof Activity)) {
			TngUtil.moveUp((Activity) parent, obj, getEClasses(), actionMgr);
		}
		if ((parent != null) && (parent instanceof WorkProductDescriptor)) {
			WorkProduct wp = ((WorkProductDescriptor) parent).getWorkProduct();
			if (wp instanceof Artifact) {
				Artifact artifact = (Artifact) wp;
				List list = artifact.getContainedArtifacts();
				WorkProduct objWP = ((WorkProductDescriptor) obj)
						.getWorkProduct();

				int location = 0;
				// List newList = new ArrayList();
				for (int i = 0; i < list.size(); i++) {
					if (objWP.equals(list.get(i))) {
						location = i;
						break;
					}
				}

				((EList) artifact.getContainedArtifacts()).move(location,
						location - 1);
			}

			Activity activity = this.getActivity(obj);
			TngUtil.moveUp(activity, obj, getEClasses(), actionMgr);
		}
	}

	public void moveDown(Object obj, IActionManager actionMgr) {
		Object parent = this.getParent(obj);
		if ((parent != null) && (parent instanceof Activity)) {
			TngUtil.moveDown((Activity) parent, obj, getEClasses(), actionMgr);
		}
		if ((parent != null) && (parent instanceof WorkProductDescriptor)) {
			WorkProduct wp = ((WorkProductDescriptor) parent).getWorkProduct();
			if (wp instanceof Artifact) {
				Artifact artifact = (Artifact) wp;
				List list = artifact.getContainedArtifacts();
				WorkProduct objWP = ((WorkProductDescriptor) obj)
						.getWorkProduct();

				int location = 0;
				// List newList = new ArrayList();
				for (int i = 0; i < list.size(); i++) {
					if (objWP.equals(list.get(i))) {
						location = i;
						break;
					}
				}

				((EList) artifact.getContainedArtifacts()).move(location,
						location + 1);
			}

			Activity activity = this.getActivity(obj);
			TngUtil.moveDown(activity, obj, getEClasses(), actionMgr);
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.ibm.library.edit.process.BreakdownElementItemProvider#createCreateCopyCommand(org.eclipse.emf.edit.domain.EditingDomain,
	 *      org.eclipse.emf.ecore.EObject,
	 *      org.eclipse.emf.edit.command.CopyCommand.Helper)
	 */
	protected Command createCreateCopyCommand(EditingDomain domain,
			EObject owner, Helper helper) {
		return new WorkProductDescriptorCreateCopyCommand(domain, owner, helper);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.emf.edit.provider.ItemProviderAdapter#factorRemoveCommand(org.eclipse.emf.edit.domain.EditingDomain, org.eclipse.emf.edit.command.CommandParameter)
	 */
	protected Command factorRemoveCommand(EditingDomain domain, CommandParameter commandParameter) {
		if(!(commandParameter.getOwner() instanceof Activity)) {
			Activity act = getActivity(target);
			if(act != null) {
				IEditingDomainItemProvider adapter = (IEditingDomainItemProvider) adapterFactory.adapt(act, IEditingDomainItemProvider.class);
				commandParameter.setOwner(act);
				return adapter.createCommand(act, domain, RemoveCommand.class, commandParameter);				
			}
		}
		return super.factorRemoveCommand(domain, commandParameter);
	}
	
	public String getAttribute(Object object, String property) {
		// EPF bug#: 144820 Some nested work product descriptors show wrong model info in work product usage view
		// for defect, override the method, previously DescriptorItemProvide.getAttribute use to have this code, moved to here. 
		if (property == IBSItemProvider.COL_MODEL_INFO) {
			StringBuffer modelInfo = new StringBuffer();
				// RATLC00383376
				if (object instanceof WorkProductDescriptor) {
//					ProcessUtil.getModelInfoForWorkProductDescriptor(modelInfo,
//							(WorkProductDescriptor) object);
					ProcessUtil.getWPDModelInfo(modelInfo, object, this);
				}
//			}
			return modelInfo.toString(); //$NON-NLS-1$
		}
		return super.getAttribute(object, property);
	}

}
