/*******************************************************************************
 * Copyright (c) 2007-2010 Red Hat, Inc.
 * Distributed under license by Red Hat, Inc. All rights reserved.
 * This program is 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:
 *     Red Hat, Inc. - initial API and implementation
 ******************************************************************************/

package org.jboss.tools.jsf.jsf2.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.wst.xml.core.internal.document.ElementImpl;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMAttr;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.jboss.tools.jsf.jsf2.model.JSF2ComponentModelManager;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
 * 
 * @author yzhishko
 * 
 */

@SuppressWarnings("restriction")
public class JSF2ComponentUtil {

	public static Map<String, List<Element>> findCompositeComponents(Node node) {
		Map<String, List<Element>> elementsMap = new HashMap<String, List<Element>>(
				0);
		findChildCompositeComponents(node, elementsMap);
		return elementsMap;
	}

	private static void findChildCompositeComponents(Node node,
			Map<String, List<Element>> elementsMap) {
		if (node instanceof IDOMDocument) {
			node = ((IDOMDocument) node).getDocumentElement();
			findChildCompositeComponents(node, elementsMap);
		} else if (node instanceof ElementImpl) {
			ElementImpl elementImpl = (ElementImpl) node;
			String namespaceURI = elementImpl.getNamespaceURI();
			if (namespaceURI != null
					&& namespaceURI
							.startsWith(JSF2ResourceUtil.JSF2_URI_PREFIX)
					&& !namespaceURI.equals(JSF2ResourceUtil.JSF2_URI_PREFIX)) {
				List<Element> elements = elementsMap.get(namespaceURI);
				if (elements == null) {
					elements = new ArrayList<Element>(0);
				}
				elements.add(elementImpl);
				elementsMap.put(namespaceURI, elements);
			}
			NodeList children = node.getChildNodes();
			if (children != null) {
				for (int i = 0; i < children.getLength(); i++) {
					findChildCompositeComponents(children.item(i), elementsMap);
				}
			}
		}
	}

	public static IDOMAttr[] findURIContainers(Node scanNode) {
		List<IDOMAttr> attrs = new ArrayList<IDOMAttr>(0);
		findChildURIContainers(scanNode, attrs);
		return attrs.toArray(new IDOMAttr[0]);
	}

	private static void findChildURIContainers(Node node,
			List<IDOMAttr> attrsList) {
		if (node instanceof IDOMDocument) {
			node = ((IDOMDocument) node).getDocumentElement();
			findChildURIContainers(node, attrsList);
		} else if (node instanceof ElementImpl) {
			ElementImpl elementImpl = (ElementImpl) node;
			NamedNodeMap attrsMap = elementImpl.getAttributes();
			if (attrsMap != null && attrsMap.getLength() != 0) {
				for (int i = 0; i < attrsMap.getLength(); i++) {
					IDOMAttr attr = (IDOMAttr) attrsMap.item(i);
					String attrValue = attr.getValue();
					if (attrValue != null
							&& attrValue
									.indexOf(JSF2ResourceUtil.JSF2_URI_PREFIX) > -1) {
						String compPath = attrValue.replaceFirst(
								JSF2ResourceUtil.JSF2_URI_PREFIX, ""); //$NON-NLS-1$
						if (!"".equals(compPath.trim())) { //$NON-NLS-1$
							if (isCorrectCompositeShemaAttrName(attr.getName())) {
								attrsList.add(attr);
							}
						}
					}
				}
			}
			NodeList children = node.getChildNodes();
			if (children != null) {
				for (int i = 0; i < children.getLength(); i++) {
					findChildURIContainers((IDOMNode) children.item(i),
							attrsList);
				}
			}
		}
	}

	private static boolean isCorrectCompositeShemaAttrName(String attrName) {
		if (attrName == null) {
			return false;
		}
		if ("xmlns".equals(attrName.trim()) || "uri".equals(attrName.trim())) { //$NON-NLS-1$ //$NON-NLS-2$
			return true;
		}
		if (attrName.indexOf(':') < 0) {
			return false;
		}
		attrName = attrName.substring(0, attrName.indexOf(':')).trim();
		if ("xmlns".equals(attrName)) { //$NON-NLS-1$
			return true;
		}
		return false;
	}

	public static Map<IFile, List<IDOMNode>> findCompositeComponentsWithURI(
			IResource resource, String URI) throws CoreException {
		Map<IFile, List<IDOMNode>> nodeMap = new HashMap<IFile, List<IDOMNode>>();
		findCompositeComponentsWithURI(resource, nodeMap, URI);
		return nodeMap;
	}

	private static void findCompositeComponentsWithURI(IResource resource,
			Map<IFile, List<IDOMNode>> nodeMap, String URI)
			throws CoreException {
		if (resource instanceof IFile) {
			IFile file = (IFile) resource;
			IDOMDocument document = JSF2ComponentModelManager
					.getReadableDOMDocument(file);
			Map<String, List<Element>> map = findCompositeComponents(document);
			Set<Entry<String, List<Element>>> entries = map.entrySet();
			List<IDOMNode> nodes = new ArrayList<IDOMNode>();
			for (Iterator<Entry<String, List<Element>>> iterator = entries
					.iterator(); iterator.hasNext();) {
				Entry<String, List<Element>> entry = (Entry<String, List<Element>>) iterator
						.next();
				if (URI.equals(entry.getKey())) {
					for (Element element : entry.getValue()) {
						if (element instanceof IDOMNode) {
							nodes.add((IDOMNode) element);
						}
					}
				}
			}
			if (!nodes.isEmpty()) {
				nodeMap.put(file, nodes);
			}
		} else if (resource instanceof IProject) {
			IResource[] children = ((IProject) resource).members();
			if (children != null) {
				for (int i = 0; i < children.length; i++) {
					findCompositeComponentsWithURI(children[i], nodeMap, URI);
				}
			}
		} else if (resource instanceof IFolder) {
			IResource[] children = ((IFolder) resource).members();
			if (children != null) {
				for (int i = 0; i < children.length; i++) {
					findCompositeComponentsWithURI(children[i], nodeMap, URI);
				}
			}
		}
	}
	
	public static IDOMElement findCompositeImpl(Node node){
		IDOMElement[] compositeImpl = new IDOMElement[1];
		findCompositeImpl(node, compositeImpl);
		return compositeImpl[0];
	}
	
	private static void findCompositeImpl(Node node,
			IDOMElement[] interfaceElement) {
		if (node instanceof IDOMDocument) {
			IDOMDocument document = (IDOMDocument) node;
			findCompositeImpl(document.getDocumentElement(),
					interfaceElement);
		}
		if (node instanceof ElementImpl) {
			ElementImpl impl = (ElementImpl) node;
			String nameSpace = impl.getNamespaceURI();
			if (JSF2ResourceUtil.JSF2_URI_PREFIX.equals(nameSpace)) {
				String nodeName = impl.getLocalName();
				if ("implementation".equals(nodeName)) { //$NON-NLS-1$
					interfaceElement[0] = impl;
					return;
				}
			} else {
				NodeList nodeList = node.getChildNodes();
				if (nodeList != null) {
					for (int i = 0; i < nodeList.getLength(); i++) {
						findCompositeImpl(nodeList.item(i),
								interfaceElement);
					}
				}
			}
		}
	}
	
	public static IDOMAttr[] extractAttrsWithValue(IDOMElement elToExtract, String value){
		List<IDOMAttr> attrs = new ArrayList<IDOMAttr>();
		extractAttrsWithValue(elToExtract, value, attrs);
		return attrs.toArray(new IDOMAttr[0]);
	}
	
	private static void extractAttrsWithValue(IDOMElement elToExtract, String value, List<IDOMAttr> attrs){
		NamedNodeMap namedNodeMap = elToExtract.getAttributes();
		if (namedNodeMap != null) {
			for (int i = 0; i < namedNodeMap.getLength(); i++) {
				IDOMAttr attr = (IDOMAttr) namedNodeMap.item(i);
				if (value.equals(attr.getValue().trim())) {
					attrs.add(attr);
				}
			}
		}
		NodeList children = elToExtract.getChildNodes();
		if (children != null) {
			for (int i = 0; i < children.getLength(); i++) {
				Node node = children.item(i);
				if (node instanceof IDOMElement) {
					extractAttrsWithValue((IDOMElement) node, value, attrs);
				}
			}
		}
	}
	
}
