/*******************************************************************************
 * 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 API and implementation
 *******************************************************************************/
package org.eclipse.ui.internal.intro.universal;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.util.Enumeration;
import java.util.Hashtable;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.Platform;
import org.eclipse.ui.internal.intro.impl.model.loader.IntroContentParser;
import org.eclipse.ui.internal.intro.universal.util.BundleUtil;
import org.eclipse.ui.internal.intro.universal.util.Log;
import org.osgi.framework.Bundle;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;


public class IntroData {
	private String productId;
	private Hashtable pages=new Hashtable();
	private boolean active;
	
	public IntroData(String productId, String fileNameOrData, boolean active) {
		this.productId = productId;
		this.active = active;
		if (fileNameOrData!=null)
			initialize(fileNameOrData);
	}
	
	public String getProductId() {
		return productId;
	}
	
	public PageData getPage(String pageId) {
		return (PageData)pages.get(pageId);
	}

	public boolean isActive() {
		return active;
	}

	private void initialize(String fileNameOrData) {
		Document doc = parse(fileNameOrData);
		if (doc == null)
			return;
		Element root = doc.getDocumentElement();
		NodeList pages = root.getChildNodes();
		for (int i = 0; i < pages.getLength(); i++) {
			Node node = pages.item(i);
			if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("page")) { //$NON-NLS-1$
				loadPage((Element) node);
			}
		}
	}

	private void loadPage(Element page) {
		PageData pd = new PageData(page);
		pages.put(pd.getId(), pd);
	}
	
	public void addImplicitContent() {
		IConfigurationElement [] elements = Platform.getExtensionRegistry().getConfigurationElementsFor("org.eclipse.ui.intro.configExtension"); //$NON-NLS-1$
		for (int i=0; i<elements.length; i++) {
			IConfigurationElement element = elements[i];
			if (element.getName().equals("configExtension")) { //$NON-NLS-1$
				String cid = element.getAttribute("configId"); //$NON-NLS-1$
				if (cid!=null && cid.equals("org.eclipse.ui.intro.universalConfig")) { //$NON-NLS-1$
					addCandidate(element);
				}
			}
		}
	}

	private void addCandidate(IConfigurationElement element) {
		String fileName = element.getAttribute("content"); //$NON-NLS-1$
		if (fileName==null)
			return;
		String bundleId = element.getDeclaringExtension().getNamespaceIdentifier();
		Bundle bundle = Platform.getBundle(bundleId);
		if (bundle==null)
			return;
		String content = BundleUtil.getResolvedResourceLocation("", fileName, //$NON-NLS-1$
	                bundle);
	    IntroContentParser parser = new IntroContentParser(content);
	    Document dom = parser.getDocument();
	    // dom can be null if the content file cannot be found
	    if (dom==null)
	    	return;
	    Element root = dom.getDocumentElement();
	    Element extension = null;
	    NodeList children = root.getChildNodes();
	    for (int i=0; i<children.getLength(); i++) {
	       	Node child = children.item(i);
	       	if (child.getNodeType()==Node.ELEMENT_NODE) {
	       		Element el = (Element)child;
	       		if (el.getNodeName().equalsIgnoreCase("extensionContent")) { //$NON-NLS-1$
	       			extension = el;
	       			break;
	       		}
	       	}
	    }
	    if (extension==null)
	       	return;
	    String id = extension.getAttribute("id"); //$NON-NLS-1$
	    String name = extension.getAttribute("name"); //$NON-NLS-1$
	    String path = extension.getAttribute("path"); //$NON-NLS-1$
	    if (id==null || path==null)
	       	return;
	    int at = path.lastIndexOf("/@"); //$NON-NLS-1$
	    if (at == -1)
	       	return;
	    if (path.charAt(path.length()-1)!='@')
	    	return;
	    String pageId = path.substring(0, at);
	    PageData pd = (PageData)pages.get(pageId);
	    if (pd==null) {
	    	pd = new PageData(pageId);
	    	pages.put(pageId, pd);
	    }
	    pd.addImplicitExtension(id, name);
	}

	private Document parse(String fileNameOrData) {
		Document document = null;
		try {
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
			docFactory.setValidating(false);
			// if this is not set, Document.getElementsByTagNameNS() will fail.
			docFactory.setNamespaceAware(true);
			docFactory.setExpandEntityReferences(false);
			DocumentBuilder parser = docFactory.newDocumentBuilder();

			if (fileNameOrData.charAt(0)=='<') {
				//This is actual content, not the file name
				StringReader reader = new StringReader(fileNameOrData);
				document = parser.parse(new InputSource(reader));
			}
			else
				document = parser.parse(fileNameOrData);
			return document;

		} catch (SAXParseException spe) {
			StringBuffer buffer = new StringBuffer("IntroData error in line "); //$NON-NLS-1$
			buffer.append(spe.getLineNumber());
			buffer.append(", uri "); //$NON-NLS-1$
			buffer.append(spe.getSystemId());
			buffer.append("\n"); //$NON-NLS-1$   
			buffer.append(spe.getMessage());

			// Use the contained exception.
			Exception x = spe;
			if (spe.getException() != null)
				x = spe.getException();
			Log.error(buffer.toString(), x);

		} catch (SAXException sxe) {
			Exception x = sxe;
			if (sxe.getException() != null)
				x = sxe.getException();
			Log.error(x.getMessage(), x);

		} catch (ParserConfigurationException pce) {
			// Parser with specified options can't be built
			Log.error(pce.getMessage(), pce);

		} catch (IOException ioe) {
			Log.error(ioe.getMessage(), ioe);
		}
		return null;
	}
	
	public void write(PrintWriter writer) {
		writer.println("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); //$NON-NLS-1$
		writer.println("<extensions>"); //$NON-NLS-1$
		for (Enumeration keys = pages.keys(); keys.hasMoreElements();) {
			String id = (String)keys.nextElement();
			PageData pd = (PageData)pages.get(id);
			pd.write(writer, "   "); //$NON-NLS-1$
		}
		writer.println("</extensions>"); //$NON-NLS-1$
	}
}
