/*******************************************************************************
 * Copyright (c) 2003, 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 v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * IBM Corporation - initial API and implementation
 *******************************************************************************/

package org.eclipse.jst.j2ee.internal.webservice.helper;

import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.jst.j2ee.common.QName;
import org.eclipse.jst.j2ee.internal.webservices.WSDLServiceExtManager;
import org.eclipse.jst.j2ee.internal.webservices.WSDLServiceHelper;
import org.eclipse.jst.j2ee.webservice.wsclient.ComponentScopedRefs;
import org.eclipse.jst.j2ee.webservice.wsclient.ServiceRef;
import org.eclipse.jst.j2ee.webservice.wsclient.WebServicesClient;
import org.eclipse.wst.common.componentcore.ComponentCore;
import org.eclipse.wst.common.componentcore.resources.IVirtualComponent;
import org.eclipse.wst.common.componentcore.resources.IVirtualFolder;

/**
 * This class contains methods to help navigate WSDL files provided in various JSR-109 descriptors
 */
public class WSDLHelper {

	private IProject proj;

	public WSDLHelper(IProject aProject) {
		this.proj = aProject;
	}

	/**
	 * This method returns the list of wsdl:port@name's contained in the WSDL file referred to by
	 * the specified <service-ref>
	 * 
	 * @param serviceRefName
	 *            The name of the <service-ref>
	 * @param compScopedRefName
	 *            The name of the <component-scoped-refs>
	 * @param wsc
	 *            The root object of the webservicesclient.xml descriptor
	 * @param exclude
	 *            port names to exclude in the returned array
	 * @return String[] An array of wsdl:port@names's. null is returned if there are any problems
	 *         with navigating the webservicesclient.xml or the WSDL.
	 */
	public String[] getPortNames(String serviceRefName, String compScopedRefName, WebServicesClient wsc, String[] exclude) {
		ServiceRef sr = getServiceRef(serviceRefName, compScopedRefName, wsc);
		if (sr == null)
			return null;

		String wsdlURL = getWSDLFileURL(sr);
		if (wsdlURL.length() == 0)
			return null;

		String serviceLocalName = getServiceQname(sr);
		String[] portNames = getPortNames(wsdlURL, serviceLocalName);
		portNames = trim(portNames, exclude);
		return portNames;

	}

	/**
	 * This method returns the namespace of the wsdl:service in WSDL file referred to by the
	 * specified <service-ref>
	 * 
	 * @param serviceRefName
	 *            The name of the <service-ref>
	 * @param compScopedRefName
	 *            The name of the <component-scoped-refs>
	 * @param wsc
	 *            The root object of the webservicesclient.xml descriptor
	 * @param exclude
	 *            port names to exclude in the returned array
	 * @return String The namespace of the wsdl:service. null is returned if there are any problems
	 *         with navigating the webservicesclient.xml or the WSDL.
	 */
	public String getPortNamespace(String serviceRefName, String compScopedRefName, WebServicesClient wsc) {
		ServiceRef sr = getServiceRef(serviceRefName, compScopedRefName, wsc);
		if (sr == null)
			return null;

		String wsdlURL = getWSDLFileURL(sr);
		if (wsdlURL.length() == 0)
			return ""; //$NON-NLS-1$

		String serviceLocalName = getServiceQname(sr);
		String namespace = getPortNamespace(wsdlURL, serviceLocalName);
		return namespace;
	}

	/**
	 * This method returns the list of wsdl:port@name's contained in the specified wsdl:service in
	 * the specified WSDL file.
	 * 
	 * @param wsdlURL
	 *            The URL of the WSDL file.
	 * @param serviceLocalName
	 *            The local name of the wsdl:service to look in.
	 * @return String[] An array of wsdl:port@name's. Null is returned a wsdl:service with the
	 *         specified local name could not be found.
	 */
	private String[] getPortNames(String wsdlURL, String serviceLocalName) {
		WSDLServiceHelper serviceHelper = WSDLServiceExtManager.getServiceHelper();
		Object targetService = getTargetService(wsdlURL, serviceLocalName);

		if (targetService == null) {
			//System.out.println("getPortNames ... targetService null.");
			return null;
		}

		Map ports = serviceHelper.getServicePorts(targetService);
		int numberOfPorts = ports.size();
		String[] portNames = new String[numberOfPorts];
		Iterator k = ports.values().iterator();
		for (int m = 0; m < numberOfPorts; m++) {
			Object port = k.next();
			portNames[m] = serviceHelper.getPortName(port);
		}

		return portNames;
	}

	/**
	 * This method returns the namespace of the specified wsdl:service in the specified WSDL file.
	 * 
	 * @param wsdlURL
	 *            The URL of the WSDL file.
	 * @param serviceLocalName
	 *            The local name of the wsdl:service to look in.
	 * @return String[] An array of wsdl:port@name's. An empty String is returned if a wsdl:service
	 *         with the specified local name could not be found.
	 */
	private String getPortNamespace(String wsdlURL, String serviceLocalName) {
		Object targetService = getTargetService(wsdlURL, serviceLocalName);

		if (targetService == null) {
			//System.out.println("getPortNamespace ... targetService null.");
			return ""; //$NON-NLS-1$
		}

		return WSDLServiceExtManager.getServiceHelper().getServiceNamespaceURI(targetService);
	}

	private Object getTargetService(String wsdlURL, String serviceLocalName) {
		WSDLServiceHelper serviceHelper = WSDLServiceExtManager.getServiceHelper();
		Object definition = getWSDLDefinition(wsdlURL);
		if (definition == null) {
			return null;
		}

		Object targetService = null;
		Map services = serviceHelper.getDefinitionServices(definition);
		if (!services.isEmpty()) {
			if ((serviceLocalName == null || serviceLocalName.length() == 0) && services.size() > 1) //Multiple
			// services
			// without
			// one
			// designated
			// is
			// an
			// error
			{
				return null;
			}

			if ((serviceLocalName == null || serviceLocalName.length() == 0) && services.size() == 1) {
				//Return the port names in the one service
				Iterator i = services.values().iterator();
				targetService = i.next();
			} else //There are multiple services but one is designated
			{
				Iterator j = services.values().iterator();
				while (j.hasNext()) {
					Object service = j.next();
					String serviceName = serviceHelper.getServiceLocalPart(service);
					if (serviceName.equals(serviceLocalName)) {
						targetService = service;
						break; //Found a match. Break out of loop. We must be assuming unique names
						// on services.
					}
				}
			}
		}

		return targetService;

	}


	private Object getWSDLDefinition(String wsdlURL) {
		WSDLServiceHelper serviceHelper = WSDLServiceExtManager.getServiceHelper();
		return serviceHelper.getWSDLDefinition(wsdlURL);
		
		/*try {
			WSDLResourceImpl res = (WSDLResourceImpl) WorkbenchResourceHelperBase.getResource(URI.createURI(wsdlURL), true);
			if (res == null)
				return null;
			return res.getDefinition();
		} catch (Exception wsdle) {
			return null;
		}*/
	}

	/**
	 * Returns the file URL of the <wsdl-file>specified in the given <service-ref>. Returns a blank
	 * String if <wsdl-file>is not specified in the provided <service-ref>
	 */
	private String getWSDLFileURL(ServiceRef sr) {
		String wsdlFileRelPath = sr.getWsdlFile();
		if (wsdlFileRelPath == null || wsdlFileRelPath.length() == 0)
			return ""; //$NON-NLS-1$

		// Get a handle on the WSDL file.
		IVirtualComponent component = ComponentCore.createComponent(proj);
		if (component != null) {
			IVirtualFolder moduleRoot = component.getRootFolder().getFolder("/"); //$NON-NLS-1$
			IFile wsdlFile = moduleRoot.getFile(wsdlFileRelPath).getUnderlyingFile();
			if (!wsdlFile.exists()) {
				return ""; //$NON-NLS-1$
			}
			//Get the fileURL from wsdlFile
			String wsdlURL;
			try {
				wsdlURL = wsdlFile.getLocation().toFile().toURL().toString();
			} catch (MalformedURLException murle) {
				return ""; //$NON-NLS-1$
			}

			if (wsdlURL == null || wsdlURL.length() == 0)
				return ""; //$NON-NLS-1$
			return wsdlURL;
		}
		return ""; //$NON-NLS-1$
	}

	/**
	 * Returns the local part of the <service-qname>specified in the given <service-ref>. Returns a
	 * blank String if <service-qname>is not specified in the provided <service-ref>
	 */
	private String getServiceQname(ServiceRef sr) {
		QName serviceQname = sr.getServiceQname();
		if (serviceQname == null)
			return ""; //$NON-NLS-1$

		String serviceLocalName = serviceQname.getLocalPart();
		if (serviceLocalName == null)
			return ""; //$NON-NLS-1$

		return serviceLocalName;
	}

	/* If compScopedRefName is empty or null, we may have to check all of them. */
	public ServiceRef getServiceRef(String name, String compScopedRefName, WebServicesClient wsc) {
		Iterator serviceRefs;
		if (compScopedRefName != null && compScopedRefName.length() > 0) {
			ComponentScopedRefs csr = getCompScopedRef(compScopedRefName, wsc);
			serviceRefs = csr.getServiceRefs().iterator();
		} else {
			serviceRefs = wsc.getServiceRefs().iterator();
			//If there are no service refs ... check all component-scoped-refs for the service-ref
			if (!serviceRefs.hasNext()) {
				Iterator csRefs = wsc.getComponentScopedRefs().iterator();

				while (csRefs.hasNext()) {
					ComponentScopedRefs csr = (ComponentScopedRefs) csRefs.next();
					Iterator srs = csr.getServiceRefs().iterator();
					while (srs.hasNext()) {
						ServiceRef sr = (ServiceRef) srs.next();
						String thisName = sr.getServiceRefName();
						if (thisName.equals(name)) {
							return sr;
						}
					}
				}

				return null; //Could not find it in any of the component-scoped-refs
			}

		}

		while (serviceRefs.hasNext()) {
			ServiceRef serviceRef = (ServiceRef) serviceRefs.next();
			String serviceRefName = serviceRef.getServiceRefName();
			if (serviceRefName.equals(name))
				return serviceRef;
		}
		return null;
	}


	private ComponentScopedRefs getCompScopedRef(String name, WebServicesClient wsc) {
		Iterator compScopedRefs = wsc.getComponentScopedRefs().iterator();
		while (compScopedRefs.hasNext()) {
			ComponentScopedRefs compScopedRef = (ComponentScopedRefs) compScopedRefs.next();
			String compScopedRefName = compScopedRef.getComponentName();
			if (compScopedRefName.equals(name))
				return compScopedRef;
		}
		return null;

	}


	private String[] trim(String[] values, String[] exclude) {
		if (values == null || values.length == 0 || exclude == null || exclude.length == 0) //no
			// change
			// required.
			// return
			// as
			// is.
			return values;

		String[] finalValues;
		ArrayList finalList = new ArrayList();
		for (int i = 0; i < values.length; i++) {
			if (!contains(exclude, values[i])) {
				finalList.add(values[i]);
			}
		}

		int finalSize = finalList.size();
		finalValues = new String[finalSize];
		Object[] finalArray = finalList.toArray();
		for (int j = 0; j < finalSize; j++) {
			finalValues[j] = (String) (finalArray[j]);
		}

		return finalValues;

	}

	/**
	 * Returns true if the array of String's contains the specified String. False otherwise.
	 */
	private boolean contains(String[] values, String s) {
		if (values == null)
			return false;

		for (int i = 0; i < values.length; i++) {
			if (values[i].equals(s))
				return true;
		}

		return false;
	}

}
