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

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.AdapterFactory;
import org.eclipse.emf.common.notify.impl.AdapterImpl;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.edit.provider.AdapterFactoryTreeIterator;
import org.eclipse.emf.edit.provider.ITreeItemContentProvider;
import org.eclipse.emf.edit.provider.ItemProviderAdapter;
import org.eclipse.epf.library.edit.process.BreakdownElementWrapperItemProvider;
import org.eclipse.epf.library.edit.process.IBSItemProvider;
import org.eclipse.epf.uma.Activity;
import org.eclipse.epf.uma.BreakdownElement;
import org.eclipse.epf.uma.Process;
import org.eclipse.epf.uma.UmaPackage;
import org.eclipse.epf.uma.VariabilityElement;
import org.eclipse.epf.uma.WorkBreakdownElement;
import org.eclipse.epf.uma.WorkOrder;


/**
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class PredecessorList extends ArrayList {
	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 3617853092570412082L;

	public static final PredecessorList EMPTY_LIST = new PredecessorList() {

		/**
		 * Comment for <code>serialVersionUID</code>
		 */
		private static final long serialVersionUID = 3904676098217097016L;

		public void refresh() {

		}

		public void add(int index, Object element) {

		}

		public boolean add(Object o) {
			throw new UnsupportedOperationException();
		}

		public boolean addAll(java.util.Collection c) {
			throw new UnsupportedOperationException();
		}

		public boolean addAll(int index, java.util.Collection c) {
			throw new UnsupportedOperationException();
		}

	};

	private AdapterFactory adapterFactory;

	private Object object;

	private Adapter listener = new AdapterImpl() {
		public void notifyChanged(org.eclipse.emf.common.notify.Notification msg) {
			switch (msg.getFeatureID(BreakdownElement.class)) {
			case UmaPackage.WORK_BREAKDOWN_ELEMENT__LINK_TO_PREDECESSOR:
				refresh();
				return;
			}
		}
	};

	private PredecessorList() {

	}

	public PredecessorList(AdapterFactory adapterFactory, Object object) {
		this.adapterFactory = adapterFactory;
		this.object = object;
		BreakdownElement e = (BreakdownElement) TngUtil.unwrap(object);
		e.eAdapters().add(0, listener);
		initialize();
	}

	public void dispose() {
		Object e = TngUtil.unwrap(object);
		if (e instanceof EObject) {
			((EObject) e).eAdapters().remove(listener);
		}
		clear();
	}

	/**
	 * Gets the right top item in the breakdown structure tree to search for
	 * item providers of predecessors
	 * 
	 * @return
	 */
	private Object getTopItem() {
		if (object instanceof BreakdownElementWrapperItemProvider) {
			BreakdownElementWrapperItemProvider itemProvider = (BreakdownElementWrapperItemProvider) object;
			if (itemProvider.isReadOnly()) {
				// this represents an inherited breakdown element
				//
				BreakdownElement e = (BreakdownElement) TngUtil.unwrap(object);
				Process proc = TngUtil.getOwningProcess(e);

				Object top = itemProvider;
				for (Object parent = itemProvider.getParent(object); parent != null;) {
					top = parent;
					BreakdownElement parentElement = (BreakdownElement) TngUtil
							.unwrap(parent);
					Process parentProc = TngUtil
							.getOwningProcess(parentElement);
					if (parentProc != proc) {
						break;
					}
					if (parent instanceof ITreeItemContentProvider) {
						parent = ((ITreeItemContentProvider) parent)
								.getParent(parent);
					} else {
						ITreeItemContentProvider adapter = (ITreeItemContentProvider) adapterFactory
								.adapt(parent, ITreeItemContentProvider.class);
						parent = adapter.getParent(parent);
					}
				}
				return top;
			} else {
				return itemProvider.getTopItem();
			}
		}
		else {
			IBSItemProvider adapter = (IBSItemProvider) adapterFactory.adapt(
					object, ITreeItemContentProvider.class);
			return adapter.getTopItem();
		}
	}

	private void initialize() {

		if (TngUtil.unwrap(object) instanceof WorkBreakdownElement) {
			WorkBreakdownElement e = (WorkBreakdownElement) TngUtil
					.unwrap(object);
			List workOrders = e.getLinkToPredecessor();
			if (workOrders.isEmpty())
				return;
			Object top = getTopItem();
			AdapterFactoryTreeIterator iter = new AdapterFactoryTreeIterator(
					adapterFactory, top);

			// copy the tree to a map of breakdown element / item provider for
			// reuse
			//
			HashMap map = new HashMap();
			while (iter.hasNext()) {
				Object obj = iter.next();
				Object be = TngUtil.unwrap(obj);
				Object ip = adapterFactory.adapt(obj,
						ITreeItemContentProvider.class);
				map.put(be, ip);
				if (be instanceof VariabilityElement) {
					VariabilityElement ve = (VariabilityElement) be;
					if (ve.getVariabilityBasedOnElement() != null) {
						map.put(ve.getVariabilityBasedOnElement(), ip);
					}
				}
			}
			if (!workOrders.isEmpty()) {
				int n = workOrders.size();
				for (int i = 0; i < n; i++) {
					WorkOrder workOrder = (WorkOrder) workOrders.get(i);
					BreakdownElement pred = workOrder.getPred();

					IBSItemProvider bsItemProvider = (IBSItemProvider) map
							.get(pred);
					if (bsItemProvider != null) {
						add(bsItemProvider);
					}
				}
			}
		}
	}

	protected void refresh() {
		clear();
		initialize();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.util.AbstractCollection#toString()
	 */
	public String toString() {
		return toUnSuppressedString(null);
	}

	public String toUnSuppressedString(Suppression sup) {
		// remove the item providers of the deleted elements
		//
		for (Iterator iter = iterator(); iter.hasNext();) {
			Object e = TngUtil.unwrap(iter.next());
			if (e instanceof ItemProviderAdapter) {
				e = ((ItemProviderAdapter) e).getTarget();
				if(e == null) {
					// object deleted
					//
					iter.remove();
				}
			}
			if (e instanceof BreakdownElement) {
				BreakdownElement be = (BreakdownElement) e;
				Activity superAct = be.getSuperActivities();
				if(superAct == null && TngUtil.getOwningProcess(be) != be) {
					iter.remove();
				}
			}
		}

		if (isEmpty())
			return ""; //$NON-NLS-1$

		StringBuffer strBuf = new StringBuffer();
		int n = size() - 1;
		for (int i = 0; i < n; i++) {
			IBSItemProvider bsItemProvider = (IBSItemProvider) get(i);
			if ((sup == null) || !sup.isSuppressed(bsItemProvider)) {
				strBuf.append(bsItemProvider.getId()).append(',');
			}
		}
		IBSItemProvider bsItemProvider = (IBSItemProvider) get(n);
		if ((sup == null) || !sup.isSuppressed(bsItemProvider)) {
			strBuf.append(bsItemProvider.getId());
		}

		return strBuf.toString();
	}

}
