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

import java.util.Iterator;
import java.util.List;

import org.eclipse.emf.common.CommonPlugin;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.common.util.UniqueEList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.InternalEList;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecore.xmi.impl.XMIHelperImpl;
import org.eclipse.epf.resourcemanager.ResourceDescriptor;
import org.eclipse.epf.uma.MethodConfiguration;
import org.eclipse.epf.uma.ecore.impl.MultiResourceEObject;

/**
 * @author Phong Nguyen Le
 * @since 1.0
 */
public class MultiFileXMIHelperImpl extends XMIHelperImpl {

	/**
	 * Platform-specific line separator.
	 */
	private static final String LINE_SEP = System.getProperty("line.separator"); //$NON-NLS-1$

	protected boolean logError = true;

	protected List sameDocReferences;

	private boolean discardUnresolvedRef;

	/**
	 * Creates a new instance.
	 */
	public MultiFileXMIHelperImpl(MultiFileXMIResourceImpl xmiRes) {
		super(xmiRes);
		sameDocReferences = new UniqueEList();
		Boolean b = (Boolean) ((MultiFileResourceSetImpl) xmiRes
				.getResourceSet()).getDefaultSaveOptions().get(
				MultiFileXMISaveImpl.DISCARD_UNRESOLVED_REFERENCES);
		discardUnresolvedRef = b != null ? b.booleanValue() : false;
	}
	
	/* (non-Javadoc)
	 * @see org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl#setResource(org.eclipse.emf.ecore.xmi.XMLResource)
	 */
	public void setResource(XMLResource resource) {
		super.setResource(resource);
		if(resource instanceof MultiFileXMIResourceImpl) {
			resourceURI = ((MultiFileXMIResourceImpl)resource).getFinalURI();
			deresolve = resourceURI != null && !resourceURI.isRelative() && resourceURI.isHierarchical();
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.emf.ecore.xmi.impl.XMLHelperImpl#getHREF(org.eclipse.emf.ecore.EObject)
	 */
	public String getHREF(EObject obj) {
		if (discardUnresolvedRef) {
			// remove unresolved references
			//
			if (obj instanceof MultiResourceEObject
					&& !((MultiResourceEObject) obj).isValid()) {
				return null;
			}
		}

		if (obj instanceof MultiResourceEObject) {
			InternalEObject o = (InternalEObject) obj;

			URI objectURI = o.eProxyURI();
			if (objectURI == null) {
				Resource otherResource = obj.eResource();
				if (otherResource == null) {
					objectURI = handleDanglingHREF(obj);
					if (objectURI == null) {
						return null;
					}
				} else if (resource == otherResource) {
					return "#" + resource.getURIFragment(obj); //$NON-NLS-1$
				} else {
					if (obj instanceof MethodConfiguration) {
						// Special handling for MethodConfiguration.
						// See
						// org.eclipse.epf.persistence.MultiFileResourceSetImpl.loadConfigurations()
						//
						EObject lib = obj.eContainer();
						if (lib != null) {
							otherResource = lib.eResource();
						} else {
							return null;
						}
					}
					if (otherResource != null) {
						ResourceDescriptor resDesc = MultiFileSaveUtil
								.getResourceDescriptor(otherResource);
						if (resDesc != null) {
							objectURI = MultiFileURIConverter.createURI(
									resDesc.getId()).appendFragment(
									getURIFragment(otherResource, obj));
							return objectURI.toString();
						} else {
							MultiFileSaveUtil.getFinalURI(otherResource).appendFragment(getURIFragment(otherResource, obj));
						}
					}
				}
			} else {
				if (!objectURI.scheme().equals(MultiFileURIConverter.SCHEME)) {
					String fragment = objectURI.fragment();
					if (resource instanceof MultiFileXMIResourceImpl
							&& ((MultiFileXMIResourceImpl) resource)
									.getEObjectByID(fragment) != null) {
						return "#" + fragment; //$NON-NLS-1$
					} else {
						objectURI = deresolve(objectURI);
					}
				}
				return objectURI.toString();
			}
		}

		return super.getHREF(obj);
	}

	protected static class ProxyReference {
		private InternalEObject owner;

		private EReference reference;

		private InternalEObject proxy;

		/**
		 * @param owner
		 * @param reference
		 * @param proxy
		 */
		public ProxyReference(InternalEObject owner, EReference reference,
				InternalEObject proxy) {
			super();
			this.owner = owner;
			this.reference = reference;
			this.proxy = proxy;
		}

		public InternalEObject getOwner() {
			return owner;
		}

		public InternalEObject getProxy() {
			return proxy;
		}

		public EReference getReference() {
			return reference;
		}

	}

	public void setValue(EObject object, EStructuralFeature feature,
			Object value, int position) {
		// TODO: more efficient way to do this replacement
		// should look at MultiFileXMISaveImpl.MyEscape.convertText()
		//
		if (value instanceof String) {
			String str = (String) value;
			if (str.indexOf("\n") > 0) { //$NON-NLS-1$
				value = str.replaceAll("\n", LINE_SEP); //$NON-NLS-1$
			}
		}
		;

		int kind = getFeatureKind(feature);

		try {
			// TODO: temporary fix to avoid ArrayIndexOutOfBoundsException,
			// needs revisit
			//
			if (kind == IS_MANY_MOVE) {
				List list = (List) object.eGet(feature);
				if (position > -1 && list.indexOf(value) == -1) {
					return;
				}
			}

			super.setValue(object, feature, value, position);

			switch (kind) {
			case IS_MANY_ADD:
			case IS_MANY_MOVE:
				InternalEList list = (InternalEList) object.eGet(feature);

				// save same document forward (unidirectional) references to
				// resolve at the end of document parsing
				//				
				if (feature instanceof EReference) {
					EReference ref = (EReference) feature;
					if (ref.getEOpposite() != null) {
						InternalEObject owner = (InternalEObject) object;
						for (Iterator iter = list.basicIterator(); iter
								.hasNext();) {
							InternalEObject element = (InternalEObject) iter
									.next();
							if (element.eIsProxy()
									&& element.eProxyURI().trimFragment()
											.equals(resourceURI)) {
								sameDocReferences.add(new ProxyReference(owner,
										ref, element));
							}
						}
					}
				}

				break;
			}
		} catch (RuntimeException e) {
			if (logError) {
				e.printStackTrace();
				CommonPlugin.INSTANCE.log(e);
				String errMsg = new StringBuffer(
						"MultiFileXMIHelperImpl.setValue(): ERROR") //$NON-NLS-1$
						.append("\n  object: ").append(object) //$NON-NLS-1$
						.append("\n  feature: ").append(feature) //$NON-NLS-1$
						.append("\n  value: ").append(value) //$NON-NLS-1$
						.append("\n  position: ").append(position) //$NON-NLS-1$
						.append("\n  kind: ").append(kind) //$NON-NLS-1$
						.toString();
				System.err.println(errMsg);
				CommonPlugin.INSTANCE.log(errMsg);
			}
			throw e;
		}
	}

}
